-
Notifications
You must be signed in to change notification settings - Fork 3.2k
create_batch feature implementation (#6256) #6324
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2d22a1a
11b3333
4f310ea
47df21b
dfd4e39
7e58a0c
fc69d42
e4be05e
e702c76
aa56698
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,11 +8,17 @@ | |
| import calendar | ||
| import json | ||
| import six | ||
| from enum import Enum | ||
| import logging | ||
|
|
||
| from uamqp import BatchMessage, Message, types | ||
| from azure.eventhub.error import EventDataError | ||
| from uamqp import BatchMessage, Message, types, constants, errors | ||
| from uamqp.message import MessageHeader, MessageProperties | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
| # event_data.encoded_size < 255, batch encode overhead is 5, >=256, overhead is 8 each | ||
| _BATCH_MESSAGE_OVERHEAD_COST = [5, 8] | ||
|
|
||
|
|
||
| def parse_sas_token(sas_token): | ||
| """Parse a SAS token into its components. | ||
|
|
@@ -51,7 +57,7 @@ class EventData(object): | |
| PROP_TIMESTAMP = b"x-opt-enqueued-time" | ||
| PROP_DEVICE_ID = b"iothub-connection-device-id" | ||
|
|
||
| def __init__(self, **kwargs): | ||
| def __init__(self, body=None, **kwargs): | ||
| """ | ||
| Initialize EventData. | ||
|
|
||
|
|
@@ -64,7 +70,6 @@ def __init__(self, **kwargs): | |
| :param message: The received message. | ||
| :type message: ~uamqp.message.Message | ||
| """ | ||
| body = kwargs.get("body", None) | ||
| to_device = kwargs.get("to_device", None) | ||
| message = kwargs.get("message", None) | ||
|
|
||
|
|
@@ -251,10 +256,40 @@ def encode_message(self): | |
| return self.message.encode_message() | ||
|
|
||
|
|
||
| class _BatchSendEventData(EventData): | ||
| def __init__(self, batch_event_data, partition_key=None): | ||
| self.message = BatchMessage(data=batch_event_data, multi_messages=False, properties=None) | ||
| class EventDataBatch(object): | ||
| """ | ||
| The EventDataBatch class is a holder of a batch of event data within max size bytes. | ||
| Use ~azure.eventhub.Producer.create_batch method to create an EventDataBatch object. | ||
| Do not instantiate an EventDataBatch object directly. | ||
| """ | ||
|
|
||
| def __init__(self, **kwargs): | ||
| max_size = kwargs.get("max_size", None) | ||
| partition_key = kwargs.get("partition_key", None) | ||
| self.max_size = max_size or constants.MAX_MESSAGE_LENGTH_BYTES | ||
| self._partition_key = partition_key | ||
| self.message = BatchMessage(data=[], multi_messages=False, properties=None) | ||
|
|
||
| self._set_partition_key(partition_key) | ||
| self._size = self.message.gather()[0].get_message_encoded_size() | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please double check the uAMQP code for
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It works in our case |
||
| self._count = 0 | ||
|
|
||
| def __len__(self): | ||
| return self._count | ||
|
|
||
| @property | ||
| def size(self): | ||
| """The size in bytes | ||
|
|
||
| :return: int | ||
| """ | ||
| return self._size | ||
|
|
||
| @staticmethod | ||
| def _from_batch(batch_data, partition_key=None): | ||
| batch_data_instance = EventDataBatch(partition_key=partition_key) | ||
| batch_data_instance.message._body_gen = batch_data | ||
| return batch_data_instance | ||
|
|
||
| def _set_partition_key(self, value): | ||
| if value: | ||
|
|
@@ -267,6 +302,38 @@ def _set_partition_key(self, value): | |
| self.message.annotations = annotations | ||
| self.message.header = header | ||
|
|
||
| def try_add(self, event_data): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is this function used?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The try_add function is part of the API and supposed to be used by customers not by us. |
||
| """ | ||
| The message size is a sum up of body, properties, header, etc. | ||
| :param event_data: | ||
| :return: | ||
| """ | ||
| if event_data is None: | ||
| log.warning("event_data is None when calling EventDataBatch.try_add. Ignored") | ||
| return | ||
| if not isinstance(event_data, EventData): | ||
| raise TypeError('event_data should be type of EventData') | ||
|
|
||
| if self._partition_key: | ||
| if event_data.partition_key and not (event_data.partition_key == self._partition_key): | ||
| raise EventDataError('The partition_key of event_data does not match the one of the EventDataBatch') | ||
| if not event_data.partition_key: | ||
| event_data._set_partition_key(self._partition_key) | ||
|
|
||
| event_data_size = event_data.message.get_message_encoded_size() | ||
|
|
||
| # For a BatchMessage, if the encoded_message_size of event_data is < 256, then the overhead cost to encode that | ||
| # message into the BatchMessage would be 5 bytes, if >= 256, it would be 8 bytes. | ||
| size_after_add = self._size + event_data_size\ | ||
| + _BATCH_MESSAGE_OVERHEAD_COST[0 if (event_data_size < 256) else 1] | ||
|
|
||
| if size_after_add > self.max_size: | ||
| raise ValueError("EventDataBatch has reached its size limit {}".format(self.max_size)) | ||
|
|
||
| self.message._body_gen.append(event_data) # pylint: disable=protected-access | ||
| self._size = size_after_add | ||
| self._count += 1 | ||
|
|
||
|
|
||
| class EventPosition(object): | ||
| """ | ||
|
|
@@ -286,7 +353,7 @@ class EventPosition(object): | |
| >>> event_pos = EventPosition(1506968696002) | ||
| """ | ||
|
|
||
| def __init__(self, value, **kwargs): | ||
| def __init__(self, value, inclusive=False): | ||
| """ | ||
| Initialize EventPosition. | ||
|
|
||
|
|
@@ -295,7 +362,6 @@ def __init__(self, value, **kwargs): | |
| :param inclusive: Whether to include the supplied value as the start point. | ||
| :type inclusive: bool | ||
| """ | ||
| inclusive = kwargs.get("inclusive", False) | ||
| self.value = value if value is not None else "-1" | ||
| self.inclusive = inclusive | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean that in the case of EventDataBatch we ignore the passed in partition key?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, EventDataBatch wins. It also has partition_key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case we should raise an error if a user tries to pass in the partition_key alongside a Batch object. Otherwise they might think the value they pass in being used when it's actually being ignored.