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,492 @@
1
+ # Copyright 2017, 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
+ from __future__ import absolute_import
16
+
17
+ import logging
18
+ import threading
19
+ import time
20
+ import typing
21
+ from typing import Any, Callable, List, Optional, Sequence
22
+ from datetime import datetime
23
+
24
+ from opentelemetry import trace
25
+ import google.api_core.exceptions
26
+ from google.api_core import gapic_v1
27
+ from google.auth import exceptions as auth_exceptions
28
+
29
+ from google.cloud.pubsub_v1.publisher import exceptions
30
+ from google.cloud.pubsub_v1.publisher import futures
31
+ from google.cloud.pubsub_v1.publisher._batch import base
32
+ from google.pubsub_v1 import types as gapic_types
33
+ from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import (
34
+ PublishMessageWrapper,
35
+ )
36
+
37
+ if typing.TYPE_CHECKING: # pragma: NO COVER
38
+ from google.cloud import pubsub_v1
39
+ from google.cloud.pubsub_v1 import types
40
+ from google.cloud.pubsub_v1.publisher import Client as PublisherClient
41
+ from google.pubsub_v1.services.publisher.client import OptionalRetry
42
+
43
+ _LOGGER = logging.getLogger(__name__)
44
+ _CAN_COMMIT = (base.BatchStatus.ACCEPTING_MESSAGES, base.BatchStatus.STARTING)
45
+ _SERVER_PUBLISH_MAX_BYTES = 10 * 1000 * 1000 # max accepted size of PublishRequest
46
+
47
+ _raw_proto_pubbsub_message = gapic_types.PubsubMessage.pb()
48
+
49
+
50
+ class Batch(base.Batch):
51
+ """A batch of messages.
52
+
53
+ The batch is the internal group of messages which are either awaiting
54
+ publication or currently in progress.
55
+
56
+ A batch is automatically created by the PublisherClient when the first
57
+ message to be published is received; subsequent messages are added to
58
+ that batch until the process of actual publishing _starts_.
59
+
60
+ Once this occurs, any new messages sent to :meth:`publish` open a new
61
+ batch.
62
+
63
+ If you are using this library, you most likely do not need to instantiate
64
+ batch objects directly; they will be created for you. If you want to
65
+ change the actual batching settings, see the ``batching`` argument on
66
+ :class:`~.pubsub_v1.PublisherClient`.
67
+
68
+ Any properties or methods on this class which are not defined in
69
+ :class:`~.pubsub_v1.publisher.batch.BaseBatch` should be considered
70
+ implementation details.
71
+
72
+ Args:
73
+ client:
74
+ The publisher client used to create this batch.
75
+ topic:
76
+ The topic. The format for this is ``projects/{project}/topics/{topic}``.
77
+ settings:
78
+ The settings for batch publishing. These should be considered immutable
79
+ once the batch has been opened.
80
+ batch_done_callback:
81
+ Callback called when the response for a batch publish has been received.
82
+ Called with one boolean argument: successfully published or a permanent
83
+ error occurred. Temporary errors are not surfaced because they are retried
84
+ at a lower level.
85
+ commit_when_full:
86
+ Whether to commit the batch when the batch is full.
87
+ commit_retry:
88
+ Designation of what errors, if any, should be retried when commiting
89
+ the batch. If not provided, a default retry is used.
90
+ commit_timeout:
91
+ The timeout to apply when commiting the batch. If not provided, a default
92
+ timeout is used.
93
+ """
94
+
95
+ _OPEN_TELEMETRY_TRACER_NAME: str = "google.cloud.pubsub_v1"
96
+ _OPEN_TELEMETRY_MESSAGING_SYSTEM: str = "gcp_pubsub"
97
+
98
+ def __init__(
99
+ self,
100
+ client: "PublisherClient",
101
+ topic: str,
102
+ settings: "types.BatchSettings",
103
+ batch_done_callback: Optional[Callable[[bool], Any]] = None,
104
+ commit_when_full: bool = True,
105
+ commit_retry: "OptionalRetry" = gapic_v1.method.DEFAULT,
106
+ commit_timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT,
107
+ ):
108
+ self._client = client
109
+ self._topic = topic
110
+ self._settings = settings
111
+ self._batch_done_callback = batch_done_callback
112
+ self._commit_when_full = commit_when_full
113
+
114
+ self._state_lock = threading.Lock()
115
+ # These members are all communicated between threads; ensure that
116
+ # any writes to them use the "state lock" to remain atomic.
117
+ # _futures list should remain unchanged after batch
118
+ # status changed from ACCEPTING_MESSAGES to any other
119
+ # in order to avoid race conditions
120
+ self._futures: List[futures.Future] = []
121
+ self._message_wrappers: List[PublishMessageWrapper] = []
122
+ self._status = base.BatchStatus.ACCEPTING_MESSAGES
123
+
124
+ # The initial size is not zero, we need to account for the size overhead
125
+ # of the PublishRequest message itself.
126
+ self._base_request_size = gapic_types.PublishRequest(topic=topic)._pb.ByteSize()
127
+ self._size = self._base_request_size
128
+
129
+ self._commit_retry = commit_retry
130
+ self._commit_timeout = commit_timeout
131
+
132
+ # Publish RPC Span that will be set by method `_start_publish_rpc_span`
133
+ # if Open Telemetry is enabled.
134
+ self._rpc_span: Optional[trace.Span] = None
135
+
136
+ @staticmethod
137
+ def make_lock() -> threading.Lock:
138
+ """Return a threading lock.
139
+
140
+ Returns:
141
+ A newly created lock.
142
+ """
143
+ return threading.Lock()
144
+
145
+ @property
146
+ def client(self) -> "PublisherClient":
147
+ """A publisher client."""
148
+ return self._client
149
+
150
+ @property
151
+ def message_wrappers(self) -> Sequence[PublishMessageWrapper]:
152
+ """The message wrappers currently in the batch."""
153
+ return self._message_wrappers
154
+
155
+ @property
156
+ def settings(self) -> "types.BatchSettings":
157
+ """Return the batch settings.
158
+
159
+ Returns:
160
+ The batch settings. These are considered immutable once the batch has
161
+ been opened.
162
+ """
163
+ return self._settings
164
+
165
+ @property
166
+ def size(self) -> int:
167
+ """Return the total size of all of the messages currently in the batch.
168
+
169
+ The size includes any overhead of the actual ``PublishRequest`` that is
170
+ sent to the backend.
171
+
172
+ Returns:
173
+ The total size of all of the messages currently in the batch (including
174
+ the request overhead), in bytes.
175
+ """
176
+ return self._size
177
+
178
+ @property
179
+ def status(self) -> base.BatchStatus:
180
+ """Return the status of this batch.
181
+
182
+ Returns:
183
+ The status of this batch. All statuses are human-readable, all-lowercase
184
+ strings.
185
+ """
186
+ return self._status
187
+
188
+ def cancel(self, cancellation_reason: base.BatchCancellationReason) -> None:
189
+ """Complete pending futures with an exception.
190
+
191
+ This method must be called before publishing starts (ie: while the
192
+ batch is still accepting messages.)
193
+
194
+ Args:
195
+ The reason why this batch has been cancelled.
196
+ """
197
+
198
+ with self._state_lock:
199
+ assert (
200
+ self._status == base.BatchStatus.ACCEPTING_MESSAGES
201
+ ), "Cancel should not be called after sending has started."
202
+
203
+ exc = RuntimeError(cancellation_reason.value)
204
+ for future in self._futures:
205
+ future.set_exception(exc)
206
+ self._status = base.BatchStatus.ERROR
207
+
208
+ def commit(self) -> None:
209
+ """Actually publish all of the messages on the active batch.
210
+
211
+ .. note::
212
+
213
+ This method is non-blocking. It opens a new thread, which calls
214
+ :meth:`_commit`, which does block.
215
+
216
+ This synchronously sets the batch status to "starting", and then opens
217
+ a new thread, which handles actually sending the messages to Pub/Sub.
218
+
219
+ If the current batch is **not** accepting messages, this method
220
+ does nothing.
221
+ """
222
+
223
+ # Set the status to "starting" synchronously, to ensure that
224
+ # this batch will necessarily not accept new messages.
225
+ with self._state_lock:
226
+ if self._status == base.BatchStatus.ACCEPTING_MESSAGES:
227
+ self._status = base.BatchStatus.STARTING
228
+ else:
229
+ return
230
+
231
+ self._start_commit_thread()
232
+
233
+ def _start_commit_thread(self) -> None:
234
+ """Start a new thread to actually handle the commit."""
235
+ # NOTE: If the thread is *not* a daemon, a memory leak exists due to a CPython issue.
236
+ # https://github.com/googleapis/python-pubsub/issues/395#issuecomment-829910303
237
+ # https://github.com/googleapis/python-pubsub/issues/395#issuecomment-830092418
238
+ commit_thread = threading.Thread(
239
+ name="Thread-CommitBatchPublisher", target=self._commit, daemon=True
240
+ )
241
+ commit_thread.start()
242
+
243
+ def _start_publish_rpc_span(self) -> None:
244
+ tracer = trace.get_tracer(self._OPEN_TELEMETRY_TRACER_NAME)
245
+ links = []
246
+
247
+ for wrapper in self._message_wrappers:
248
+ span = wrapper.create_span
249
+ # Add links only for sampled spans.
250
+ if span.get_span_context().trace_flags.sampled:
251
+ links.append(trace.Link(span.get_span_context()))
252
+ assert len(self._topic.split("/")) == 4
253
+ topic_short_name = self._topic.split("/")[3]
254
+ with tracer.start_as_current_span(
255
+ name=f"{topic_short_name} publish",
256
+ attributes={
257
+ "messaging.system": self._OPEN_TELEMETRY_MESSAGING_SYSTEM,
258
+ "messaging.destination.name": topic_short_name,
259
+ "gcp.project_id": self._topic.split("/")[1],
260
+ "messaging.batch.message_count": len(self._message_wrappers),
261
+ "messaging.operation": "publish",
262
+ "code.function": "_commit",
263
+ },
264
+ links=links,
265
+ kind=trace.SpanKind.CLIENT,
266
+ end_on_exit=False,
267
+ ) as rpc_span:
268
+ ctx = rpc_span.get_span_context()
269
+ for wrapper in self._message_wrappers:
270
+ span = wrapper.create_span
271
+ if span.get_span_context().trace_flags.sampled:
272
+ span.add_link(ctx)
273
+ self._rpc_span = rpc_span
274
+
275
+ def _commit(self) -> None:
276
+ """Actually publish all of the messages on the active batch.
277
+
278
+ This moves the batch out from being the active batch to an in progress
279
+ batch on the publisher, and then the batch is discarded upon
280
+ completion.
281
+
282
+ .. note::
283
+
284
+ This method blocks. The :meth:`commit` method is the non-blocking
285
+ version, which calls this one.
286
+ """
287
+ with self._state_lock:
288
+ if self._status in _CAN_COMMIT:
289
+ self._status = base.BatchStatus.IN_PROGRESS
290
+ else:
291
+ # If, in the intervening period between when this method was
292
+ # called and now, the batch started to be committed, or
293
+ # completed a commit, then no-op at this point.
294
+ _LOGGER.debug(
295
+ "Batch is already in progress or has been cancelled, "
296
+ "exiting commit"
297
+ )
298
+ return
299
+
300
+ # Once in the IN_PROGRESS state, no other thread can publish additional
301
+ # messages or initiate a commit (those operations become a no-op), thus
302
+ # it is safe to release the state lock here. Releasing the lock avoids
303
+ # blocking other threads in case api.publish() below takes a long time
304
+ # to complete.
305
+ # https://github.com/googleapis/google-cloud-python/issues/8036
306
+
307
+ # Sanity check: If there are no messages, no-op.
308
+ if not self._message_wrappers:
309
+ _LOGGER.debug("No messages to publish, exiting commit")
310
+ self._status = base.BatchStatus.SUCCESS
311
+ return
312
+
313
+ # Begin the request to publish these messages.
314
+ # Log how long the underlying request takes.
315
+ start = time.time()
316
+
317
+ batch_transport_succeeded = True
318
+ try:
319
+ if self._client.open_telemetry_enabled:
320
+ self._start_publish_rpc_span()
321
+
322
+ # Performs retries for errors defined by the retry configuration.
323
+ response = self._client._gapic_publish(
324
+ topic=self._topic,
325
+ messages=[wrapper.message for wrapper in self._message_wrappers],
326
+ retry=self._commit_retry,
327
+ timeout=self._commit_timeout,
328
+ )
329
+
330
+ if self._client.open_telemetry_enabled:
331
+ assert self._rpc_span is not None
332
+ self._rpc_span.end()
333
+ end_time = str(datetime.now())
334
+ for message_id, wrapper in zip(
335
+ response.message_ids, self._message_wrappers
336
+ ):
337
+ span = wrapper.create_span
338
+ span.add_event(
339
+ name="publish end",
340
+ attributes={
341
+ "timestamp": end_time,
342
+ },
343
+ )
344
+ span.set_attribute(key="messaging.message.id", value=message_id)
345
+ wrapper.end_create_span()
346
+ except (
347
+ google.api_core.exceptions.GoogleAPIError,
348
+ auth_exceptions.TransportError,
349
+ ) as exc:
350
+ # We failed to publish, even after retries, so set the exception on
351
+ # all futures and exit.
352
+ self._status = base.BatchStatus.ERROR
353
+
354
+ if self._client.open_telemetry_enabled:
355
+ if self._rpc_span:
356
+ self._rpc_span.record_exception(
357
+ exception=exc,
358
+ )
359
+ self._rpc_span.set_status(
360
+ trace.Status(status_code=trace.StatusCode.ERROR)
361
+ )
362
+ self._rpc_span.end()
363
+
364
+ for wrapper in self._message_wrappers:
365
+ wrapper.end_create_span(exc=exc)
366
+
367
+ batch_transport_succeeded = False
368
+ if self._batch_done_callback is not None:
369
+ # Failed to publish batch.
370
+ self._batch_done_callback(batch_transport_succeeded)
371
+
372
+ for future in self._futures:
373
+ future.set_exception(exc)
374
+
375
+ return
376
+
377
+ end = time.time()
378
+ _LOGGER.debug("gRPC Publish took %s seconds.", end - start)
379
+
380
+ if len(response.message_ids) == len(self._futures):
381
+ # Iterate over the futures on the queue and return the response
382
+ # IDs. We are trusting that there is a 1:1 mapping, and raise
383
+ # an exception if not.
384
+ self._status = base.BatchStatus.SUCCESS
385
+ for message_id, future in zip(response.message_ids, self._futures):
386
+ future.set_result(message_id)
387
+ else:
388
+ # Sanity check: If the number of message IDs is not equal to
389
+ # the number of futures I have, then something went wrong.
390
+ self._status = base.BatchStatus.ERROR
391
+ exception = exceptions.PublishError(
392
+ "Some messages were not successfully published."
393
+ )
394
+
395
+ for future in self._futures:
396
+ future.set_exception(exception)
397
+
398
+ # Unknown error -> batch failed to be correctly transported/
399
+ batch_transport_succeeded = False
400
+
401
+ _LOGGER.error(
402
+ "Only %s of %s messages were published.",
403
+ len(response.message_ids),
404
+ len(self._futures),
405
+ )
406
+
407
+ if self._batch_done_callback is not None:
408
+ self._batch_done_callback(batch_transport_succeeded)
409
+
410
+ def publish(
411
+ self,
412
+ wrapper: PublishMessageWrapper,
413
+ ) -> Optional["pubsub_v1.publisher.futures.Future"]:
414
+ """Publish a single message.
415
+
416
+ Add the given message to this object; this will cause it to be
417
+ published once the batch either has enough messages or a sufficient
418
+ period of time has elapsed. If the batch is full or the commit is
419
+ already in progress, the method does not do anything.
420
+
421
+ This method is called by :meth:`~.PublisherClient.publish`.
422
+
423
+ Args:
424
+ wrapper: The Pub/Sub message wrapper.
425
+
426
+ Returns:
427
+ An object conforming to the :class:`~concurrent.futures.Future` interface
428
+ or :data:`None`. If :data:`None` is returned, that signals that the batch
429
+ cannot accept a message.
430
+
431
+ Raises:
432
+ pubsub_v1.publisher.exceptions.MessageTooLargeError: If publishing
433
+ the ``message`` would exceed the max size limit on the backend.
434
+ """
435
+
436
+ # Coerce the type, just in case.
437
+ if not isinstance(
438
+ wrapper.message, gapic_types.PubsubMessage
439
+ ): # pragma: NO COVER
440
+ # For performance reasons, the message should be constructed by directly
441
+ # using the raw protobuf class, and only then wrapping it into the
442
+ # higher-level PubsubMessage class.
443
+ vanilla_pb = _raw_proto_pubbsub_message(**wrapper.message)
444
+ wrapper.message = gapic_types.PubsubMessage.wrap(vanilla_pb)
445
+
446
+ future = None
447
+
448
+ with self._state_lock:
449
+ assert (
450
+ self._status != base.BatchStatus.ERROR
451
+ ), "Publish after stop() or publish error."
452
+
453
+ if self.status != base.BatchStatus.ACCEPTING_MESSAGES:
454
+ return None
455
+
456
+ size_increase = gapic_types.PublishRequest(
457
+ messages=[wrapper.message]
458
+ )._pb.ByteSize()
459
+
460
+ if (self._base_request_size + size_increase) > _SERVER_PUBLISH_MAX_BYTES:
461
+ err_msg = (
462
+ "The message being published would produce too large a publish "
463
+ "request that would exceed the maximum allowed size on the "
464
+ "backend ({} bytes).".format(_SERVER_PUBLISH_MAX_BYTES)
465
+ )
466
+ raise exceptions.MessageTooLargeError(err_msg)
467
+
468
+ new_size = self._size + size_increase
469
+ new_count = len(self._message_wrappers) + 1
470
+
471
+ size_limit = min(self.settings.max_bytes, _SERVER_PUBLISH_MAX_BYTES)
472
+ overflow = new_size > size_limit or new_count >= self.settings.max_messages
473
+
474
+ if not self._message_wrappers or not overflow:
475
+ # Store the actual message in the batch's message queue.
476
+ self._message_wrappers.append(wrapper)
477
+ self._size = new_size
478
+
479
+ # Track the future on this batch (so that the result of the
480
+ # future can be set).
481
+ future = futures.Future()
482
+ self._futures.append(future)
483
+
484
+ # Try to commit, but it must be **without** the lock held, since
485
+ # ``commit()`` will try to obtain the lock.
486
+ if self._commit_when_full and overflow:
487
+ self.commit()
488
+
489
+ return future
490
+
491
+ def _set_status(self, status: base.BatchStatus):
492
+ self._status = status
@@ -0,0 +1,82 @@
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
+ from __future__ import absolute_import
16
+
17
+ import abc
18
+ import typing
19
+
20
+ from google.api_core import gapic_v1
21
+ from google.pubsub_v1 import types as gapic_types
22
+
23
+ if typing.TYPE_CHECKING: # pragma: NO COVER
24
+ from concurrent import futures
25
+ from google.pubsub_v1.services.publisher.client import OptionalRetry
26
+
27
+
28
+ class Sequencer(metaclass=abc.ABCMeta):
29
+ """The base class for sequencers for Pub/Sub publishing. A sequencer
30
+ sequences messages to be published.
31
+ """
32
+
33
+ @abc.abstractmethod
34
+ def is_finished(self) -> bool: # pragma: NO COVER
35
+ """Whether the sequencer is finished and should be cleaned up.
36
+
37
+ Returns:
38
+ bool: Whether the sequencer is finished and should be cleaned up.
39
+ """
40
+ raise NotImplementedError
41
+
42
+ @abc.abstractmethod
43
+ def unpause(self) -> None: # pragma: NO COVER
44
+ """Unpauses this sequencer.
45
+
46
+ Raises:
47
+ RuntimeError:
48
+ If called when the sequencer has not been paused.
49
+ """
50
+ raise NotImplementedError
51
+
52
+ @abc.abstractmethod
53
+ def publish(
54
+ self,
55
+ message: gapic_types.PubsubMessage,
56
+ retry: "OptionalRetry" = gapic_v1.method.DEFAULT,
57
+ timeout: gapic_types.TimeoutType = gapic_v1.method.DEFAULT,
58
+ ) -> "futures.Future": # pragma: NO COVER
59
+ """Publish message for this ordering key.
60
+
61
+ Args:
62
+ message:
63
+ The Pub/Sub message.
64
+ retry:
65
+ The retry settings to apply when publishing the message.
66
+ timeout:
67
+ The timeout to apply when publishing the message.
68
+
69
+ Returns:
70
+ A class instance that conforms to Python Standard library's
71
+ :class:`~concurrent.futures.Future` interface. The future might return
72
+ immediately with a
73
+ `pubsub_v1.publisher.exceptions.PublishToPausedOrderingKeyException`
74
+ if the ordering key is paused. Otherwise, the future tracks the
75
+ lifetime of the message publish.
76
+
77
+ Raises:
78
+ RuntimeError:
79
+ If called after this sequencer has been stopped, either by
80
+ a call to stop() or after all batches have been published.
81
+ """
82
+ raise NotImplementedError