|
13 | 13 | # limitations under the License.
|
14 | 14 |
|
15 | 15 | from __future__ import absolute_import
|
| 16 | +from __future__ import division |
16 | 17 |
|
17 | 18 | import collections
|
| 19 | +import itertools |
18 | 20 | import logging
|
| 21 | +import math |
19 | 22 | import threading
|
20 | 23 |
|
21 | 24 | from google.cloud.pubsub_v1 import types
|
|
34 | 37 | """The maximum amount of time in seconds to wait for additional request items
|
35 | 38 | before processing the next batch of requests."""
|
36 | 39 |
|
| 40 | +_ACK_IDS_BATCH_SIZE = 2500 |
| 41 | +"""The maximum number of ACK IDs to send in a single StreamingPullRequest. |
| 42 | +
|
| 43 | +The backend imposes a maximum request size limit of 524288 bytes (512 KiB) per |
| 44 | +acknowledge / modifyAckDeadline request. ACK IDs have a maximum size of 164 |
| 45 | +bytes, thus we cannot send more than o 524288/176 ~= 2979 ACK IDs in a single |
| 46 | +StreamingPullRequest message. |
| 47 | +
|
| 48 | +Accounting for some overhead, we should thus only send a maximum of 2500 ACK |
| 49 | +IDs at a time. |
| 50 | +""" |
| 51 | + |
37 | 52 |
|
38 | 53 | class Dispatcher(object):
|
39 | 54 | def __init__(self, manager, queue):
|
@@ -119,9 +134,16 @@ def ack(self, items):
|
119 | 134 | if time_to_ack is not None:
|
120 | 135 | self._manager.ack_histogram.add(time_to_ack)
|
121 | 136 |
|
122 |
| - ack_ids = [item.ack_id for item in items] |
123 |
| - request = types.StreamingPullRequest(ack_ids=ack_ids) |
124 |
| - self._manager.send(request) |
| 137 | + # We must potentially split the request into multiple smaller requests |
| 138 | + # to avoid the server-side max request size limit. |
| 139 | + ack_ids = (item.ack_id for item in items) |
| 140 | + total_chunks = int(math.ceil(len(items) / _ACK_IDS_BATCH_SIZE)) |
| 141 | + |
| 142 | + for _ in range(total_chunks): |
| 143 | + request = types.StreamingPullRequest( |
| 144 | + ack_ids=itertools.islice(ack_ids, _ACK_IDS_BATCH_SIZE) |
| 145 | + ) |
| 146 | + self._manager.send(request) |
125 | 147 |
|
126 | 148 | # Remove the message from lease management.
|
127 | 149 | self.drop(items)
|
@@ -150,13 +172,18 @@ def modify_ack_deadline(self, items):
|
150 | 172 | Args:
|
151 | 173 | items(Sequence[ModAckRequest]): The items to modify.
|
152 | 174 | """
|
153 |
| - ack_ids = [item.ack_id for item in items] |
154 |
| - seconds = [item.seconds for item in items] |
155 |
| - |
156 |
| - request = types.StreamingPullRequest( |
157 |
| - modify_deadline_ack_ids=ack_ids, modify_deadline_seconds=seconds |
158 |
| - ) |
159 |
| - self._manager.send(request) |
| 175 | + # We must potentially split the request into multiple smaller requests |
| 176 | + # to avoid the server-side max request size limit. |
| 177 | + ack_ids = (item.ack_id for item in items) |
| 178 | + seconds = (item.seconds for item in items) |
| 179 | + total_chunks = int(math.ceil(len(items) / _ACK_IDS_BATCH_SIZE)) |
| 180 | + |
| 181 | + for _ in range(total_chunks): |
| 182 | + request = types.StreamingPullRequest( |
| 183 | + modify_deadline_ack_ids=itertools.islice(ack_ids, _ACK_IDS_BATCH_SIZE), |
| 184 | + modify_deadline_seconds=itertools.islice(seconds, _ACK_IDS_BATCH_SIZE), |
| 185 | + ) |
| 186 | + self._manager.send(request) |
160 | 187 |
|
161 | 188 | def nack(self, items):
|
162 | 189 | """Explicitly deny receipt of messages.
|
|
0 commit comments