feat(python): support message partitioning strategies - #3927
Conversation
|
Thanks for the PR. It is labeled Slash commands (own line, regular comment) move it around the queue:
See CONTRIBUTING.md for details. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3927 +/- ##
=========================================
Coverage 83.76% 83.76%
Complexity 1358 1358
=========================================
Files 1212 1213 +1
Lines 166189 166215 +26
Branches 133663 133663
=========================================
+ Hits 139210 139236 +26
Misses 23342 23342
Partials 3637 3637
🚀 New features to boost your workflow:
|
ethanlin01x
left a comment
There was a problem hiding this comment.
Some suggestions. The stub one I think should be fixed before merge.
| """ | ||
|
|
||
| @typing.final | ||
| class Partitioning: |
There was a problem hiding this comment.
Partitioning is missing from __all__, so from apache_iggy import * fails pyrefly with unknown-name.
| #[pyo3(transparent)] | ||
| Strategy(Partitioning), |
There was a problem hiding this comment.
Strategy has no annotation, so errors read 'Strategy | int' — a Rust name that does not exist in Python. Please add annotation = "Partitioning"
| ) | ||
|
|
||
| assert len(response.confirmations) == 1 | ||
| assert response.confirmations[0].partition_id < partitions_count |
There was a problem hiding this comment.
Sending once cannot tell round-robin apart from always returning partition 0. Could you send three batches and assert the three confirmed partition ids are distinct?
There was a problem hiding this comment.
I'd rather a new example like this not be created, unless it is a capability that the rust sdk does not offer. Better to add comments to an existing example writing how can message partitioning options will result in different outcomes, if any.
There was a problem hiding this comment.
Follows from above comment -- not required.
| let stream = Identifier::try_from(stream)?; | ||
| let topic = Identifier::try_from(topic)?; | ||
| let partitioning = Partitioning::partition_id(partitioning); | ||
| let partitioning = partitioning.into(); |
There was a problem hiding this comment.
Let's change this into from::. I like to be explicit about what is being converted into what in code. into does not help a first-time reader understand this without searching the code base.
| #[gen_stub_pymethods] | ||
| #[pymethods] | ||
| impl Partitioning { | ||
| /// Routes the batch to partitions using server-side round-robin selection. |
There was a problem hiding this comment.
Change to "Routes the batch to one partition selected by round-robin."
| } | ||
| } | ||
|
|
||
| /// Routes the batch to the specified partition. |
There was a problem hiding this comment.
Change to
/// Routes the batch to the specified partition.
///
/// `partition_id` must be between 0 and `2**32 - 1`. The topic must contain
/// that partition when the batch is sent.
| /// Sends a list of messages to the specified topic. | ||
| /// Returns a SendMessagesResponse carrying the per-partition commit | ||
| /// confirmations, or a PyRuntimeError on failure. The confirmation list is | ||
| /// empty when the server reports no offsets, and the legacy server never | ||
| /// reports any. | ||
| /// | ||
| /// `partitioning` is required. Pass `Partitioning.balanced()`, | ||
| /// `Partitioning.partition_id(id)`, or `Partitioning.messages_key(key)`. | ||
| /// An integer remains supported as shorthand for `partition_id`. |
There was a problem hiding this comment.
Lets change this to
/// Sends a batch of messages to a topic using the selected partitioning strategy.
///
/// Args:
/// stream: Stream identifier as `str | int`.
/// topic: Topic identifier as `str | int`.
/// partitioning: A `Partitioning` strategy or an integer partition ID.
/// Use `Partitioning.balanced()`, `Partitioning.partition_id(id)`, or
/// `Partitioning.messages_key(key)`. An integer is shorthand for
/// `Partitioning.partition_id(id)`.
/// messages: Messages to send as `list[SendMessage]`.
///
/// Returns:
/// An awaitable that resolves to `SendMessagesResponse`. Its confirmations
/// report the committed partition and batch base offset. The list is empty
/// when the server reports no offsets, including on the legacy server.
///
/// Raises:
/// ValueError: If a string stream or topic identifier is invalid.
/// TypeError: If `partitioning` or `messages` has an unsupported type.
/// OverflowError: If a numeric stream, topic, or partition ID is outside
/// the supported unsigned 32-bit range.
/// RuntimeError: If the request fails.
|
|
||
| @pytest.mark.unit | ||
| @pytest.mark.parametrize("key", [b"a" * 255, "a" * 255]) | ||
| def test_messages_key_accepts_255_bytes(self, key): |
There was a problem hiding this comment.
Let's add "界" * 85 as a case for this as well.
| @pytest.mark.unit | ||
| def test_balanced_and_partition_id_strategies(self): | ||
| assert isinstance(Partitioning.balanced(), Partitioning) | ||
| assert isinstance(Partitioning.partition_id(1), Partitioning) |
There was a problem hiding this comment.
Let's add an assertion for partition_id(2**32 -1)
| assert confirmation.base_offset == polled_messages[0].offset() | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_send_messages_with_partition_id_strategy( |
There was a problem hiding this comment.
In this test, let's also send one message with Partitioning.partition_id(3) and assert RuntimeError
There was a problem hiding this comment.
Something that did not land anywhere: In TestMessageOperations.test_send_messages_with_partition_id_strategy, send two messages instead of one and retain the existing single-confirmation assertion.
Which issue does this PR address?
Closes #3896
Rationale
The Python SDK only supported sending messages to an explicit partition, while the Rust SDK also supports balanced and message-key partitioning.
What changed?
IggyClient.send_messages()now accepts a PythonPartitioningobject supporting fixed, balanced, and message-key routing. Existing integer partition IDs remain fully compatible, while string message keys are encoded as UTF-8 and validated against the 1–255 byte limit.Type stubs, tests, and a runnable Python example covering all three strategies are included.
Local Execution
foreign/python/tests/test_message_operations.pyAI Usage