google-cloud-pubsub 2.30.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. google/cloud/pubsub/__init__.py +31 -0
  2. google/cloud/pubsub_v1/__init__.py +35 -0
  3. google/cloud/pubsub_v1/exceptions.py +20 -0
  4. google/cloud/pubsub_v1/futures.py +56 -0
  5. google/cloud/pubsub_v1/open_telemetry/__init__.py +13 -0
  6. google/cloud/pubsub_v1/open_telemetry/context_propagation.py +55 -0
  7. google/cloud/pubsub_v1/open_telemetry/publish_message_wrapper.py +142 -0
  8. google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py +280 -0
  9. google/cloud/pubsub_v1/proto/pubsub.proto +1344 -0
  10. google/cloud/pubsub_v1/proto/schema.proto +286 -0
  11. google/cloud/pubsub_v1/publisher/__init__.py +20 -0
  12. google/cloud/pubsub_v1/publisher/_batch/__init__.py +0 -0
  13. google/cloud/pubsub_v1/publisher/_batch/base.py +174 -0
  14. google/cloud/pubsub_v1/publisher/_batch/thread.py +492 -0
  15. google/cloud/pubsub_v1/publisher/_sequencer/__init__.py +0 -0
  16. google/cloud/pubsub_v1/publisher/_sequencer/base.py +82 -0
  17. google/cloud/pubsub_v1/publisher/_sequencer/ordered_sequencer.py +340 -0
  18. google/cloud/pubsub_v1/publisher/_sequencer/unordered_sequencer.py +167 -0
  19. google/cloud/pubsub_v1/publisher/client.py +614 -0
  20. google/cloud/pubsub_v1/publisher/exceptions.py +51 -0
  21. google/cloud/pubsub_v1/publisher/flow_controller.py +313 -0
  22. google/cloud/pubsub_v1/publisher/futures.py +83 -0
  23. google/cloud/pubsub_v1/subscriber/__init__.py +20 -0
  24. google/cloud/pubsub_v1/subscriber/_protocol/__init__.py +0 -0
  25. google/cloud/pubsub_v1/subscriber/_protocol/dispatcher.py +658 -0
  26. google/cloud/pubsub_v1/subscriber/_protocol/heartbeater.py +77 -0
  27. google/cloud/pubsub_v1/subscriber/_protocol/helper_threads.py +122 -0
  28. google/cloud/pubsub_v1/subscriber/_protocol/histogram.py +159 -0
  29. google/cloud/pubsub_v1/subscriber/_protocol/leaser.py +292 -0
  30. google/cloud/pubsub_v1/subscriber/_protocol/messages_on_hold.py +182 -0
  31. google/cloud/pubsub_v1/subscriber/_protocol/requests.py +62 -0
  32. google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +1309 -0
  33. google/cloud/pubsub_v1/subscriber/client.py +323 -0
  34. google/cloud/pubsub_v1/subscriber/exceptions.py +44 -0
  35. google/cloud/pubsub_v1/subscriber/futures.py +125 -0
  36. google/cloud/pubsub_v1/subscriber/message.py +532 -0
  37. google/cloud/pubsub_v1/subscriber/scheduler.py +170 -0
  38. google/cloud/pubsub_v1/types.py +336 -0
  39. google/pubsub/__init__.py +174 -0
  40. google/pubsub/gapic_version.py +16 -0
  41. google/pubsub/py.typed +2 -0
  42. google/pubsub_v1/__init__.py +172 -0
  43. google/pubsub_v1/gapic_metadata.json +591 -0
  44. google/pubsub_v1/gapic_version.py +16 -0
  45. google/pubsub_v1/py.typed +2 -0
  46. google/pubsub_v1/services/__init__.py +15 -0
  47. google/pubsub_v1/services/publisher/__init__.py +22 -0
  48. google/pubsub_v1/services/publisher/async_client.py +1670 -0
  49. google/pubsub_v1/services/publisher/client.py +2136 -0
  50. google/pubsub_v1/services/publisher/pagers.py +508 -0
  51. google/pubsub_v1/services/publisher/transports/__init__.py +38 -0
  52. google/pubsub_v1/services/publisher/transports/base.py +427 -0
  53. google/pubsub_v1/services/publisher/transports/grpc.py +667 -0
  54. google/pubsub_v1/services/publisher/transports/grpc_asyncio.py +847 -0
  55. google/pubsub_v1/services/publisher/transports/rest.py +2511 -0
  56. google/pubsub_v1/services/publisher/transports/rest_base.py +678 -0
  57. google/pubsub_v1/services/schema_service/__init__.py +22 -0
  58. google/pubsub_v1/services/schema_service/async_client.py +1783 -0
  59. google/pubsub_v1/services/schema_service/client.py +2197 -0
  60. google/pubsub_v1/services/schema_service/pagers.py +352 -0
  61. google/pubsub_v1/services/schema_service/transports/__init__.py +38 -0
  62. google/pubsub_v1/services/schema_service/transports/base.py +439 -0
  63. google/pubsub_v1/services/schema_service/transports/grpc.py +676 -0
  64. google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py +858 -0
  65. google/pubsub_v1/services/schema_service/transports/rest.py +2718 -0
  66. google/pubsub_v1/services/schema_service/transports/rest_base.py +746 -0
  67. google/pubsub_v1/services/subscriber/__init__.py +22 -0
  68. google/pubsub_v1/services/subscriber/async_client.py +2643 -0
  69. google/pubsub_v1/services/subscriber/client.py +3094 -0
  70. google/pubsub_v1/services/subscriber/pagers.py +352 -0
  71. google/pubsub_v1/services/subscriber/transports/__init__.py +38 -0
  72. google/pubsub_v1/services/subscriber/transports/base.py +589 -0
  73. google/pubsub_v1/services/subscriber/transports/grpc.py +921 -0
  74. google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +1207 -0
  75. google/pubsub_v1/services/subscriber/transports/rest.py +3561 -0
  76. google/pubsub_v1/services/subscriber/transports/rest_base.py +1024 -0
  77. google/pubsub_v1/types/__init__.py +169 -0
  78. google/pubsub_v1/types/pubsub.py +3478 -0
  79. google/pubsub_v1/types/schema.py +511 -0
  80. google_cloud_pubsub-2.30.0.data/scripts/fixup_pubsub_v1_keywords.py +210 -0
  81. google_cloud_pubsub-2.30.0.dist-info/METADATA +283 -0
  82. google_cloud_pubsub-2.30.0.dist-info/RECORD +85 -0
  83. google_cloud_pubsub-2.30.0.dist-info/WHEEL +5 -0
  84. google_cloud_pubsub-2.30.0.dist-info/licenses/LICENSE +202 -0
  85. google_cloud_pubsub-2.30.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,340 @@
1
+ # Copyright 2019, Google LLC All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import enum
16
+ import collections
17
+ import threading
18
+ import typing
19
+ from typing import Deque, Iterable, Sequence
20
+
21
+ from google.api_core import gapic_v1
22
+ from google.cloud.pubsub_v1.publisher import futures
23
+ from google.cloud.pubsub_v1.publisher import exceptions
24
+ from google.cloud.pubsub_v1.publisher._sequencer import base as sequencer_base
25
+ from google.cloud.pubsub_v1.publisher._batch import base as batch_base
26
+ from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
27
+ PublishMessageWrapper,
28
+ )
29
+
30
+ if typing.TYPE_CHECKING: # pragma: NO COVER
31
+ from google.cloud.pubsub_v1 import types
32
+ from google.cloud.pubsub_v1.publisher import _batch
33
+ from google.cloud.pubsub_v1.publisher.client import Client as PublisherClient
34
+ from google.pubsub_v1.services.publisher.client import OptionalRetry
35
+
36
+
37
+ class _OrderedSequencerStatus(str, enum.Enum):
38
+ """An enum-like class representing valid statuses for an OrderedSequencer.
39
+
40
+ Starting state: ACCEPTING_MESSAGES
41
+ Valid transitions:
42
+ ACCEPTING_MESSAGES -> PAUSED (on permanent error)
43
+ ACCEPTING_MESSAGES -> STOPPED (when user calls stop() explicitly)
44
+ ACCEPTING_MESSAGES -> FINISHED (all batch publishes finish normally)
45
+
46
+ PAUSED -> ACCEPTING_MESSAGES (when user unpauses)
47
+ PAUSED -> STOPPED (when user calls stop() explicitly)
48
+
49
+ STOPPED -> FINISHED (user stops client and the one remaining batch finishes
50
+ publish)
51
+ STOPPED -> PAUSED (stop() commits one batch, which fails permanently)
52
+
53
+ FINISHED -> ACCEPTING_MESSAGES (publish happens while waiting for cleanup)
54
+ FINISHED -> STOPPED (when user calls stop() explicitly)
55
+ Illegal transitions:
56
+ PAUSED -> FINISHED (since all batches are cancelled on pause, there should
57
+ not be any that finish normally. paused sequencers
58
+ should not be cleaned up because their presence
59
+ indicates that the ordering key needs to be resumed)
60
+ STOPPED -> ACCEPTING_MESSAGES (no way to make a user-stopped sequencer
61
+ accept messages again. this is okay since
62
+ stop() should only be called on shutdown.)
63
+ FINISHED -> PAUSED (no messages remain in flight, so they can't cause a
64
+ permanent error and pause the sequencer)
65
+ """
66
+
67
+ # Accepting publishes and/or waiting for result of batch publish
68
+ ACCEPTING_MESSAGES = "accepting messages"
69
+ # Permanent error occurred. User must unpause this sequencer to resume
70
+ # publishing. This is done to maintain ordering.
71
+ PAUSED = "paused"
72
+ # No more publishes allowed. There may be an outstanding batch that will
73
+ # call the _batch_done_callback when it's done (success or error.)
74
+ STOPPED = "stopped"
75
+ # No more work to do. Waiting to be cleaned-up. A publish will transform
76
+ # this sequencer back into the normal accepting-messages state.
77
+ FINISHED = "finished"
78
+
79
+
80
+ class OrderedSequencer(sequencer_base.Sequencer):
81
+ """Sequences messages into batches ordered by an ordering key for one topic.
82
+
83
+ A sequencer always has at least one batch in it, unless paused or stopped.
84
+ When no batches remain, the |publishes_done_callback| is called so the
85
+ client can perform cleanup.
86
+
87
+ Public methods are thread-safe.
88
+
89
+ Args:
90
+ client:
91
+ The publisher client used to create this sequencer.
92
+ topic:
93
+ The topic. The format for this is ``projects/{project}/topics/{topic}``.
94
+ ordering_key:
95
+ The ordering key for this sequencer.
96
+ """
97
+
98
+ def __init__(self, client: "PublisherClient", topic: str, ordering_key: str):
99
+ self._client = client
100
+ self._topic = topic
101
+ self._ordering_key = ordering_key
102
+ # Guards the variables below
103
+ self._state_lock = threading.Lock()
104
+ # Batches ordered from first (head/left) to last (right/tail).
105
+ # Invariant: always has at least one batch after the first publish,
106
+ # unless paused or stopped.
107
+ self._ordered_batches: Deque["_batch.thread.Batch"] = collections.deque()
108
+ # See _OrderedSequencerStatus for valid state transitions.
109
+ self._state = _OrderedSequencerStatus.ACCEPTING_MESSAGES
110
+
111
+ def is_finished(self) -> bool:
112
+ """Whether the sequencer is finished and should be cleaned up.
113
+
114
+ Returns:
115
+ Whether the sequencer is finished and should be cleaned up.
116
+ """
117
+ with self._state_lock:
118
+ return self._state == _OrderedSequencerStatus.FINISHED
119
+
120
+ def stop(self) -> None:
121
+ """Permanently stop this sequencer.
122
+
123
+ This differs from pausing, which may be resumed. Immediately commits
124
+ the first batch and cancels the rest.
125
+
126
+ Raises:
127
+ RuntimeError:
128
+ If called after stop() has already been called.
129
+ """
130
+ with self._state_lock:
131
+ if self._state == _OrderedSequencerStatus.STOPPED:
132
+ raise RuntimeError("Ordered sequencer already stopped.")
133
+
134
+ self._state = _OrderedSequencerStatus.STOPPED
135
+ if self._ordered_batches:
136
+ # Give only the first batch the chance to finish.
137
+ self._ordered_batches[0].commit()
138
+
139
+ # Cancel the rest of the batches and remove them from the deque
140
+ # of batches.
141
+ while len(self._ordered_batches) > 1:
142
+ # Pops from the tail until it leaves only the head in the
143
+ # deque.
144
+ batch = self._ordered_batches.pop()
145
+ batch.cancel(batch_base.BatchCancellationReason.CLIENT_STOPPED)
146
+
147
+ def commit(self) -> None:
148
+ """Commit the first batch, if unpaused.
149
+
150
+ If paused or no batches exist, this method does nothing.
151
+
152
+ Raises:
153
+ RuntimeError:
154
+ If called after stop() has already been called.
155
+ """
156
+ with self._state_lock:
157
+ if self._state == _OrderedSequencerStatus.STOPPED:
158
+ raise RuntimeError("Ordered sequencer already stopped.")
159
+
160
+ if self._state != _OrderedSequencerStatus.PAUSED and self._ordered_batches:
161
+ # It's okay to commit the same batch more than once. The
162
+ # operation is idempotent.
163
+ self._ordered_batches[0].commit()
164
+
165
+ def _batch_done_callback(self, success: bool) -> None:
166
+ """Deal with completion of a batch.
167
+
168
+ Called when a batch has finished publishing, with either a success
169
+ or a failure. (Temporary failures are retried infinitely when
170
+ ordering keys are enabled.)
171
+ """
172
+ ensure_cleanup_and_commit_timer_runs = False
173
+ with self._state_lock:
174
+ assert self._state != _OrderedSequencerStatus.PAUSED, (
175
+ "This method should not be called after pause() because "
176
+ "pause() should have cancelled all of the batches."
177
+ )
178
+ assert self._state != _OrderedSequencerStatus.FINISHED, (
179
+ "This method should not be called after all batches have been "
180
+ "finished."
181
+ )
182
+
183
+ # Message futures for the batch have been completed (either with a
184
+ # result or an exception) already, so remove the batch.
185
+ self._ordered_batches.popleft()
186
+
187
+ if success:
188
+ if len(self._ordered_batches) == 0:
189
+ # Mark this sequencer as finished.
190
+ # If new messages come in for this ordering key and this
191
+ # sequencer hasn't been cleaned up yet, it will go back
192
+ # into accepting-messages state. Otherwise, the client
193
+ # must create a new OrderedSequencer.
194
+ self._state = _OrderedSequencerStatus.FINISHED
195
+ # Ensure cleanup thread runs at some point.
196
+ ensure_cleanup_and_commit_timer_runs = True
197
+ elif len(self._ordered_batches) == 1:
198
+ # Wait for messages and/or commit timeout
199
+ # Ensure there's actually a commit timer thread that'll commit
200
+ # after a delay.
201
+ ensure_cleanup_and_commit_timer_runs = True
202
+ else:
203
+ # If there is more than one batch, we know that the next batch
204
+ # must be full and, therefore, ready to be committed.
205
+ self._ordered_batches[0].commit()
206
+ else:
207
+ # Unrecoverable error detected
208
+ self._pause()
209
+
210
+ if ensure_cleanup_and_commit_timer_runs:
211
+ self._client.ensure_cleanup_and_commit_timer_runs()
212
+
213
+ def _pause(self) -> None:
214
+ """Pause this sequencer: set state to paused, cancel all batches, and
215
+ clear the list of ordered batches.
216
+
217
+ _state_lock must be taken before calling this method.
218
+ """
219
+ assert (
220
+ self._state != _OrderedSequencerStatus.FINISHED
221
+ ), "Pause should not be called after all batches have finished."
222
+ self._state = _OrderedSequencerStatus.PAUSED
223
+ for batch in self._ordered_batches:
224
+ batch.cancel(
225
+ batch_base.BatchCancellationReason.PRIOR_ORDERED_MESSAGE_FAILED
226
+ )
227
+ self._ordered_batches.clear()
228
+
229
+ def unpause(self) -> None:
230
+ """Unpause this sequencer.
231
+
232
+ Raises:
233
+ RuntimeError:
234
+ If called when the ordering key has not been paused.
235
+ """
236
+ with self._state_lock:
237
+ if self._state != _OrderedSequencerStatus.PAUSED:
238
+ raise RuntimeError("Ordering key is not paused.")
239
+ self._state = _OrderedSequencerStatus.ACCEPTING_MESSAGES
240
+
241
+ def _create_batch(
242
+ self,
243
+ commit_retry: "OptionalRetry" = gapic_v1.method.DEFAULT,
244
+ commit_timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT,
245
+ ) -> "_batch.thread.Batch":
246
+ """Create a new batch using the client's batch class and other stored
247
+ settings.
248
+
249
+ Args:
250
+ commit_retry:
251
+ The retry settings to apply when publishing the batch.
252
+ commit_timeout:
253
+ The timeout to apply when publishing the batch.
254
+ """
255
+ return self._client._batch_class(
256
+ client=self._client,
257
+ topic=self._topic,
258
+ settings=self._client.batch_settings,
259
+ batch_done_callback=self._batch_done_callback,
260
+ commit_when_full=False,
261
+ commit_retry=commit_retry,
262
+ commit_timeout=commit_timeout,
263
+ )
264
+
265
+ def publish(
266
+ self,
267
+ wrapper: PublishMessageWrapper,
268
+ retry: "OptionalRetry" = gapic_v1.method.DEFAULT,
269
+ timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT,
270
+ ) -> futures.Future:
271
+ """Publish message for this ordering key.
272
+
273
+ Args:
274
+ wrapper:
275
+ The Pub/Sub message wrapper.
276
+ retry:
277
+ The retry settings to apply when publishing the message.
278
+ timeout:
279
+ The timeout to apply when publishing the message.
280
+
281
+ Returns:
282
+ A class instance that conforms to Python Standard library's
283
+ :class:`~concurrent.futures.Future` interface (but not an
284
+ instance of that class). The future might return immediately with a
285
+ PublishToPausedOrderingKeyException if the ordering key is paused.
286
+ Otherwise, the future tracks the lifetime of the message publish.
287
+
288
+ Raises:
289
+ RuntimeError:
290
+ If called after this sequencer has been stopped, either by
291
+ a call to stop() or after all batches have been published.
292
+ """
293
+ with self._state_lock:
294
+ if self._state == _OrderedSequencerStatus.PAUSED:
295
+ errored_future = futures.Future()
296
+ exception = exceptions.PublishToPausedOrderingKeyException(
297
+ self._ordering_key
298
+ )
299
+ errored_future.set_exception(exception)
300
+ return errored_future
301
+
302
+ # If waiting to be cleaned-up, convert to accepting messages to
303
+ # prevent this sequencer from being cleaned-up only to have another
304
+ # one with the same ordering key created immediately afterward.
305
+ if self._state == _OrderedSequencerStatus.FINISHED:
306
+ self._state = _OrderedSequencerStatus.ACCEPTING_MESSAGES
307
+
308
+ if self._state == _OrderedSequencerStatus.STOPPED:
309
+ raise RuntimeError("Cannot publish on a stopped sequencer.")
310
+
311
+ assert (
312
+ self._state == _OrderedSequencerStatus.ACCEPTING_MESSAGES
313
+ ), "Publish is only allowed in accepting-messages state."
314
+
315
+ if not self._ordered_batches:
316
+ new_batch = self._create_batch(
317
+ commit_retry=retry, commit_timeout=timeout
318
+ )
319
+ self._ordered_batches.append(new_batch)
320
+
321
+ batch = self._ordered_batches[-1]
322
+ future = batch.publish(wrapper)
323
+ while future is None:
324
+ batch = self._create_batch(commit_retry=retry, commit_timeout=timeout)
325
+ self._ordered_batches.append(batch)
326
+ future = batch.publish(wrapper)
327
+
328
+ return future
329
+
330
+ # Used only for testing.
331
+ def _set_batch(self, batch: "_batch.thread.Batch") -> None:
332
+ self._ordered_batches = collections.deque([batch])
333
+
334
+ # Used only for testing.
335
+ def _set_batches(self, batches: Iterable["_batch.thread.Batch"]) -> None:
336
+ self._ordered_batches = collections.deque(batches)
337
+
338
+ # Used only for testing.
339
+ def _get_batches(self) -> Sequence["_batch.thread.Batch"]:
340
+ return self._ordered_batches
@@ -0,0 +1,167 @@
1
+ # Copyright 2019, Google LLC All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import typing
16
+ from typing import Optional
17
+
18
+ from google.api_core import gapic_v1
19
+
20
+ from google.cloud.pubsub_v1.publisher._sequencer import base
21
+ from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
22
+ PublishMessageWrapper,
23
+ )
24
+
25
+ if typing.TYPE_CHECKING: # pragma: NO COVER
26
+ from google.cloud.pubsub_v1.publisher import _batch
27
+ from google.cloud.pubsub_v1.publisher import futures
28
+ from google.cloud.pubsub_v1.publisher.client import Client as PublisherClient
29
+ from google.pubsub_v1.services.publisher.client import OptionalRetry
30
+
31
+ from google.cloud.pubsub_v1 import types
32
+
33
+
34
+ class UnorderedSequencer(base.Sequencer):
35
+ """Sequences messages into batches for one topic without any ordering.
36
+
37
+ Public methods are NOT thread-safe.
38
+ """
39
+
40
+ def __init__(self, client: "PublisherClient", topic: str):
41
+ self._client = client
42
+ self._topic = topic
43
+ self._current_batch: Optional["_batch.thread.Batch"] = None
44
+ self._stopped = False
45
+
46
+ def is_finished(self) -> bool:
47
+ """Whether the sequencer is finished and should be cleaned up.
48
+
49
+ Returns:
50
+ Whether the sequencer is finished and should be cleaned up.
51
+ """
52
+ # TODO: Implement. Not implementing yet because of possible performance
53
+ # impact due to extra locking required. This does mean that
54
+ # UnorderedSequencers don't get cleaned up, but this is the same as
55
+ # previously existing behavior.
56
+ return False
57
+
58
+ def stop(self) -> None:
59
+ """Stop the sequencer.
60
+
61
+ Subsequent publishes will fail.
62
+
63
+ Raises:
64
+ RuntimeError:
65
+ If called after stop() has already been called.
66
+ """
67
+ if self._stopped:
68
+ raise RuntimeError("Unordered sequencer already stopped.")
69
+ self.commit()
70
+ self._stopped = True
71
+
72
+ def commit(self) -> None:
73
+ """Commit the batch.
74
+
75
+ Raises:
76
+ RuntimeError:
77
+ If called after stop() has already been called.
78
+ """
79
+ if self._stopped:
80
+ raise RuntimeError("Unordered sequencer already stopped.")
81
+ if self._current_batch:
82
+ self._current_batch.commit()
83
+
84
+ # At this point, we lose track of the old batch, but we don't
85
+ # care since we just committed it.
86
+ # Setting this to None guarantees the next publish() creates a new
87
+ # batch.
88
+ self._current_batch = None
89
+
90
+ def unpause(self) -> typing.NoReturn:
91
+ """Not relevant for this class."""
92
+ raise NotImplementedError
93
+
94
+ def _create_batch(
95
+ self,
96
+ commit_retry: "OptionalRetry" = gapic_v1.method.DEFAULT,
97
+ commit_timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT,
98
+ ) -> "_batch.thread.Batch":
99
+ """Create a new batch using the client's batch class and other stored
100
+ settings.
101
+
102
+ Args:
103
+ commit_retry:
104
+ The retry settings to apply when publishing the batch.
105
+ commit_timeout:
106
+ The timeout to apply when publishing the batch.
107
+ """
108
+ return self._client._batch_class(
109
+ client=self._client,
110
+ topic=self._topic,
111
+ settings=self._client.batch_settings,
112
+ batch_done_callback=None,
113
+ commit_when_full=True,
114
+ commit_retry=commit_retry,
115
+ commit_timeout=commit_timeout,
116
+ )
117
+
118
+ def publish(
119
+ self,
120
+ wrapper: PublishMessageWrapper,
121
+ retry: "OptionalRetry" = gapic_v1.method.DEFAULT,
122
+ timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT,
123
+ ) -> "futures.Future":
124
+ """Batch message into existing or new batch.
125
+
126
+ Args:
127
+ wrapper:
128
+ The Pub/Sub message wrapper.
129
+ retry:
130
+ The retry settings to apply when publishing the message.
131
+ timeout:
132
+ The timeout to apply when publishing the message.
133
+
134
+ Returns:
135
+ An object conforming to the :class:`~concurrent.futures.Future` interface.
136
+ The future tracks the publishing status of the message.
137
+
138
+ Raises:
139
+ RuntimeError:
140
+ If called after stop() has already been called.
141
+
142
+ pubsub_v1.publisher.exceptions.MessageTooLargeError: If publishing
143
+ the ``message`` would exceed the max size limit on the backend.
144
+ """
145
+ if self._stopped:
146
+ raise RuntimeError("Unordered sequencer already stopped.")
147
+
148
+ if not self._current_batch:
149
+ newbatch = self._create_batch(commit_retry=retry, commit_timeout=timeout)
150
+ self._current_batch = newbatch
151
+
152
+ batch = self._current_batch
153
+ future = None
154
+ while future is None:
155
+ # Might throw MessageTooLargeError
156
+ future = batch.publish(wrapper)
157
+ # batch is full, triggering commit_when_full
158
+ if future is None:
159
+ batch = self._create_batch(commit_retry=retry, commit_timeout=timeout)
160
+ # At this point, we lose track of the old batch, but we don't
161
+ # care since it's already committed (because it was full.)
162
+ self._current_batch = batch
163
+ return future
164
+
165
+ # Used only for testing.
166
+ def _set_batch(self, batch: "_batch.thread.Batch") -> None:
167
+ self._current_batch = batch