feat(python): expose partition management - #4017
Conversation
|
Thanks for the PR. It is labeled Slash commands (own line, regular comment) move it around the queue:
See CONTRIBUTING.md for details. |
1230cdc to
86cb5dd
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #4017 +/- ##
=========================================
Coverage 84.97% 84.98%
Complexity 1402 1402
=========================================
Files 1230 1230
Lines 181408 181444 +36
Branches 147703 147703
=========================================
+ Hits 154157 154193 +36
Misses 23199 23199
Partials 4052 4052
🚀 New features to boost your workflow:
|
|
Clean, minimal binding. It follows the existing Smaller observations
Splitting the line the same way and re-running
Nothing here blocks the merge. This review was drafted by an AI-assisted tool (Apache Magpie), so it may contain mistakes. If you think one of them is misapplied, please reply on the PR, and a maintainer will weigh in. |
86cb5dd to
54f71d5
Compare
|
@justinmclean Thanks for the careful review. I updated both new methods to document |
ethanlin01x
left a comment
There was a problem hiding this comment.
The implementation looks correct to me, but it would be better to add more test coverage.test_partition.py only covers one happy path with string ids. Could you add:
- numeric stream/topic ids — the only type conversion here is untested (see
test_consumer_group.py:101for the pattern) - assert the remaining partition ids after delete, not just the count —
delete_partitionsremoves the last N partitions_count = 0is rejected on bothcreate_partitionsanddelete_partitions— the binding does no client-side validation, so this behaviour comes from the server and is worth pinning down- deleting more partitions than the topic has is rejected
- missing stream/topic raises
RuntimeError
|
@ethanlin01x Thanks for the review. Added the requested coverage in
Revalidated with:
|
|
/ready |
a4aab32 to
ec33ea7
Compare
|
/ready |
|
/ready |
hubcio
left a comment
There was a problem hiding this comment.
small note on the PR description - the validation block says 1 passed, but the file now has 5 tests, the last four added after that run.
found other problem during review, in server: delete_partitions never rolls the deleted partitions' bytes back out of TopicStats / StreamStats - core/metadata/src/stm/stream.rs:2232 evicts the child entries only - so get_topic, get_stream and /metrics keep counting them until restart. purge does it right via zero_out_all, and delete_topic has the same gap one level up - we will fix it (ignore it in your pr)
| /// Args: | ||
| /// stream_id: Stream identifier as `str | int`. | ||
| /// topic_id: Topic identifier as `str | int`. | ||
| /// partitions_count: Number of partitions to create. |
There was a problem hiding this comment.
warning: partitions_count has no documented range. server takes 1..=1000 (MAX_PARTITIONS_PER_REQUEST) and reports 0 as "Too many partitions", so spell it out here - the stub is the only doc a python caller gets.
also at line 819.
| stream=stream_name, name=topic_name, partitions_count=2 | ||
| ) | ||
|
|
||
| with pytest.raises(RuntimeError): |
There was a problem hiding this comment.
warning: bare pytest.raises(RuntimeError) passes on any failure - every server error maps to RuntimeError here. add match=: "Too many partitions" for zero count, "Invalid partitions count" for over-count, "was not found." for missing stream/topic.
also at lines 84, 100, 117, 119, 121, 123.
| /// Args: | ||
| /// stream_id: Stream identifier as `str | int`. | ||
| /// topic_id: Topic identifier as `str | int`. | ||
| /// partitions_count: Number of partitions to delete from the end of the topic. |
There was a problem hiding this comment.
nit: partitions_count drops the "as int" suffix the other args in this same docstring carry. adding it pushes the generated stub line past 88 chars, so rewrap rather than extend.
also at line 785.
| /// partitions_count: Number of partitions to delete from the end of the topic. | ||
| /// | ||
| /// Returns: | ||
| /// An awaitable that resolves to `None` when the partitions are deleted. |
There was a problem hiding this comment.
nit: the awaitable resolves on metadata commit, not after teardown - the unlink runs later in the reconciler and can back off. say "when the deletion is accepted; storage teardown completes asynchronously".
| }) | ||
| } | ||
|
|
||
| /// Delete the last partitions from a topic, including all messages stored in them. |
There was a problem hiding this comment.
nit: neither docstring mentions the consumer group side effect - delete rebalances members off the removed partitions and drops their stats, create leaves the new ones unassigned until the next rebalance. one line on each.
also at line 780.
|
|
||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_partition_management_rejects_missing_stream_or_topic( |
There was a problem hiding this comment.
nit: the documented ValueError for a bad identifier is never exercised here, and there's no delete-all-partitions case. skip an over-cap test though - zero and over-cap return the same code, so it would duplicate the zero-count assertion.
| from apache_iggy import IggyClient | ||
|
|
||
|
|
||
| @pytest.mark.asyncio |
There was a problem hiding this comment.
nit: module-level test functions - 9 of the 10 other suites group into Test* classes. wrap these in class TestPartitionManagement:.
|
|
||
| @pytest.mark.asyncio | ||
| async def test_create_and_delete_partitions(iggy_client: IggyClient, unique_name): | ||
| stream_name = unique_name() |
There was a problem hiding this comment.
simplification: the same 7-line create_stream + create_topic preamble is copy-pasted in four tests. pull it into a module-level helper like test_consumer_group.py:37 - there is already a TODO there asking for exactly this.
| stream=stream_name, name=topic_name, partitions_count=2 | ||
| ) | ||
|
|
||
| with pytest.raises(RuntimeError): |
There was a problem hiding this comment.
simplification: these four raises blocks only differ in which identifier is missing. one parametrize over (method, missing) collapses them and keeps the test name.
| inner | ||
| .create_partitions(&stream_id, &topic_id, partitions_count) | ||
| .await | ||
| .map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?; |
There was a problem hiding this comment.
simplification: this makes 38 copies of the same PyErr::new::<PyRuntimeError, _>(e.to_string()) map_err in this file. a to_runtime_error(e: impl ToString) helper, like to_value_error in send_message.rs:93, cuts each site to .map_err(to_runtime_error)?.
also at line 843.
Closes #4014
Summary
create_partitionsanddelete_partitionson the PythonIggyClientPartitionClientmethods using the established async binding patternValidation
cargo check --manifest-path foreign/python/Cargo.tomlcargo clippy --manifest-path foreign/python/Cargo.toml --all-targets --all-features -- -D warningscargo fmt --manifest-path foreign/python/Cargo.toml -- --checkuv run --project foreign/python --no-sync ruff format --check foreign/python/tests/test_partition.pyuv run --project foreign/python --no-sync ruff check foreign/python/tests/test_partition.pycargo run -p server --bin iggy-server -- --with-default-root-credentials --fresh(with a local macOS hwloc/pkg-config build environment)uv run --project foreign/python --no-sync pytest foreign/python/tests/test_partition.py -v(1 passed)Risk boundary
The change is limited to the Python binding surface. It introduces no protocol or server behavior changes and delegates directly to the existing Rust SDK operations. The integration test verifies the observable partition count after both operations.
Disclosure: This contribution was developed with assistance from generative AI. I reviewed the implementation and ran all validation listed above locally.