google-cloud-bigtable 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 (115) hide show
  1. google/cloud/bigtable/__init__.py +25 -0
  2. google/cloud/bigtable/app_profile.py +377 -0
  3. google/cloud/bigtable/backup.py +490 -0
  4. google/cloud/bigtable/batcher.py +414 -0
  5. google/cloud/bigtable/client.py +475 -0
  6. google/cloud/bigtable/cluster.py +541 -0
  7. google/cloud/bigtable/column_family.py +362 -0
  8. google/cloud/bigtable/data/__init__.py +103 -0
  9. google/cloud/bigtable/data/_async/__init__.py +25 -0
  10. google/cloud/bigtable/data/_async/_mutate_rows.py +226 -0
  11. google/cloud/bigtable/data/_async/_read_rows.py +363 -0
  12. google/cloud/bigtable/data/_async/client.py +1522 -0
  13. google/cloud/bigtable/data/_async/mutations_batcher.py +534 -0
  14. google/cloud/bigtable/data/_cross_sync/__init__.py +20 -0
  15. google/cloud/bigtable/data/_cross_sync/_decorators.py +441 -0
  16. google/cloud/bigtable/data/_cross_sync/_mapping_meta.py +64 -0
  17. google/cloud/bigtable/data/_cross_sync/cross_sync.py +334 -0
  18. google/cloud/bigtable/data/_helpers.py +249 -0
  19. google/cloud/bigtable/data/_sync_autogen/_mutate_rows.py +182 -0
  20. google/cloud/bigtable/data/_sync_autogen/_read_rows.py +304 -0
  21. google/cloud/bigtable/data/_sync_autogen/client.py +1293 -0
  22. google/cloud/bigtable/data/_sync_autogen/mutations_batcher.py +449 -0
  23. google/cloud/bigtable/data/exceptions.py +340 -0
  24. google/cloud/bigtable/data/execute_query/__init__.py +43 -0
  25. google/cloud/bigtable/data/execute_query/_async/__init__.py +13 -0
  26. google/cloud/bigtable/data/execute_query/_async/execute_query_iterator.py +291 -0
  27. google/cloud/bigtable/data/execute_query/_byte_cursor.py +123 -0
  28. google/cloud/bigtable/data/execute_query/_checksum.py +43 -0
  29. google/cloud/bigtable/data/execute_query/_parameters_formatting.py +155 -0
  30. google/cloud/bigtable/data/execute_query/_query_result_parsing_utils.py +134 -0
  31. google/cloud/bigtable/data/execute_query/_reader.py +122 -0
  32. google/cloud/bigtable/data/execute_query/_sync_autogen/execute_query_iterator.py +239 -0
  33. google/cloud/bigtable/data/execute_query/metadata.py +399 -0
  34. google/cloud/bigtable/data/execute_query/values.py +123 -0
  35. google/cloud/bigtable/data/mutations.py +380 -0
  36. google/cloud/bigtable/data/read_modify_write_rules.py +112 -0
  37. google/cloud/bigtable/data/read_rows_query.py +536 -0
  38. google/cloud/bigtable/data/row.py +535 -0
  39. google/cloud/bigtable/data/row_filters.py +968 -0
  40. google/cloud/bigtable/encryption_info.py +64 -0
  41. google/cloud/bigtable/enums.py +223 -0
  42. google/cloud/bigtable/error.py +64 -0
  43. google/cloud/bigtable/gapic_version.py +16 -0
  44. google/cloud/bigtable/helpers.py +31 -0
  45. google/cloud/bigtable/instance.py +789 -0
  46. google/cloud/bigtable/policy.py +255 -0
  47. google/cloud/bigtable/row.py +1267 -0
  48. google/cloud/bigtable/row_data.py +380 -0
  49. google/cloud/bigtable/row_filters.py +838 -0
  50. google/cloud/bigtable/row_merger.py +250 -0
  51. google/cloud/bigtable/row_set.py +213 -0
  52. google/cloud/bigtable/table.py +1409 -0
  53. google/cloud/bigtable_admin/__init__.py +410 -0
  54. google/cloud/bigtable_admin/gapic_version.py +16 -0
  55. google/cloud/bigtable_admin/py.typed +2 -0
  56. google/cloud/bigtable_admin_v2/__init__.py +250 -0
  57. google/cloud/bigtable_admin_v2/gapic_metadata.json +962 -0
  58. google/cloud/bigtable_admin_v2/gapic_version.py +16 -0
  59. google/cloud/bigtable_admin_v2/py.typed +2 -0
  60. google/cloud/bigtable_admin_v2/services/__init__.py +15 -0
  61. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/__init__.py +22 -0
  62. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/async_client.py +3465 -0
  63. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py +3944 -0
  64. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/pagers.py +681 -0
  65. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/__init__.py +40 -0
  66. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py +751 -0
  67. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py +1249 -0
  68. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py +1579 -0
  69. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest.py +6820 -0
  70. google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest_base.py +1746 -0
  71. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/__init__.py +22 -0
  72. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py +3472 -0
  73. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py +3949 -0
  74. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/pagers.py +669 -0
  75. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/__init__.py +40 -0
  76. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py +708 -0
  77. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py +1257 -0
  78. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py +1549 -0
  79. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest.py +6609 -0
  80. google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest_base.py +1714 -0
  81. google/cloud/bigtable_admin_v2/types/__init__.py +248 -0
  82. google/cloud/bigtable_admin_v2/types/bigtable_instance_admin.py +1364 -0
  83. google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py +1715 -0
  84. google/cloud/bigtable_admin_v2/types/common.py +81 -0
  85. google/cloud/bigtable_admin_v2/types/instance.py +819 -0
  86. google/cloud/bigtable_admin_v2/types/table.py +1028 -0
  87. google/cloud/bigtable_admin_v2/types/types.py +776 -0
  88. google/cloud/bigtable_v2/__init__.py +136 -0
  89. google/cloud/bigtable_v2/gapic_metadata.json +193 -0
  90. google/cloud/bigtable_v2/gapic_version.py +16 -0
  91. google/cloud/bigtable_v2/py.typed +2 -0
  92. google/cloud/bigtable_v2/services/__init__.py +15 -0
  93. google/cloud/bigtable_v2/services/bigtable/__init__.py +22 -0
  94. google/cloud/bigtable_v2/services/bigtable/async_client.py +1741 -0
  95. google/cloud/bigtable_v2/services/bigtable/client.py +2194 -0
  96. google/cloud/bigtable_v2/services/bigtable/transports/__init__.py +38 -0
  97. google/cloud/bigtable_v2/services/bigtable/transports/base.py +343 -0
  98. google/cloud/bigtable_v2/services/bigtable/transports/grpc.py +660 -0
  99. google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py +762 -0
  100. google/cloud/bigtable_v2/services/bigtable/transports/rest.py +2489 -0
  101. google/cloud/bigtable_v2/services/bigtable/transports/rest_base.py +711 -0
  102. google/cloud/bigtable_v2/types/__init__.py +138 -0
  103. google/cloud/bigtable_v2/types/bigtable.py +1531 -0
  104. google/cloud/bigtable_v2/types/data.py +1612 -0
  105. google/cloud/bigtable_v2/types/feature_flags.py +119 -0
  106. google/cloud/bigtable_v2/types/request_stats.py +171 -0
  107. google/cloud/bigtable_v2/types/response_params.py +64 -0
  108. google/cloud/bigtable_v2/types/types.py +579 -0
  109. google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_admin_v2_keywords.py +233 -0
  110. google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_v2_keywords.py +186 -0
  111. google_cloud_bigtable-2.30.0.dist-info/LICENSE +202 -0
  112. google_cloud_bigtable-2.30.0.dist-info/METADATA +154 -0
  113. google_cloud_bigtable-2.30.0.dist-info/RECORD +115 -0
  114. google_cloud_bigtable-2.30.0.dist-info/WHEEL +5 -0
  115. google_cloud_bigtable-2.30.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,414 @@
1
+ # Copyright 2018 Google LLC
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
+ """User friendly container for Google Cloud Bigtable MutationBatcher."""
16
+ import threading
17
+ import queue
18
+ import concurrent.futures
19
+ import atexit
20
+
21
+
22
+ from google.api_core.exceptions import from_grpc_status
23
+ from dataclasses import dataclass
24
+
25
+
26
+ FLUSH_COUNT = 100 # after this many elements, send out the batch
27
+
28
+ MAX_MUTATION_SIZE = 20 * 1024 * 1024 # 20MB # after this many bytes, send out the batch
29
+
30
+ MAX_OUTSTANDING_BYTES = 100 * 1024 * 1024 # 100MB # max inflight byte size.
31
+
32
+ MAX_OUTSTANDING_ELEMENTS = 100000 # max inflight mutations.
33
+
34
+
35
+ class MutationsBatchError(Exception):
36
+ """Error in the batch request"""
37
+
38
+ def __init__(self, message, exc):
39
+ self.exc = exc
40
+ self.message = message
41
+ super().__init__(self.message)
42
+
43
+
44
+ class _MutationsBatchQueue(object):
45
+ """Private Threadsafe Queue to hold rows for batching."""
46
+
47
+ def __init__(self, max_mutation_bytes=MAX_MUTATION_SIZE, flush_count=FLUSH_COUNT):
48
+ """Specify the queue constraints"""
49
+ self._queue = queue.Queue()
50
+ self.total_mutation_count = 0
51
+ self.total_size = 0
52
+ self.max_mutation_bytes = max_mutation_bytes
53
+ self.flush_count = flush_count
54
+
55
+ def get(self):
56
+ """
57
+ Retrieve an item from the queue. Recalculate queue size.
58
+
59
+ If the queue is empty, return None.
60
+ """
61
+ try:
62
+ row = self._queue.get_nowait()
63
+ mutation_size = row.get_mutations_size()
64
+ self.total_mutation_count -= len(row._get_mutations())
65
+ self.total_size -= mutation_size
66
+ return row
67
+ except queue.Empty:
68
+ return None
69
+
70
+ def put(self, item):
71
+ """Insert an item to the queue. Recalculate queue size."""
72
+
73
+ mutation_count = len(item._get_mutations())
74
+
75
+ self._queue.put(item)
76
+
77
+ self.total_size += item.get_mutations_size()
78
+ self.total_mutation_count += mutation_count
79
+
80
+ def full(self):
81
+ """Check if the queue is full."""
82
+ if (
83
+ self.total_mutation_count >= self.flush_count
84
+ or self.total_size >= self.max_mutation_bytes
85
+ ):
86
+ return True
87
+ return False
88
+
89
+
90
+ @dataclass
91
+ class _BatchInfo:
92
+ """Keeping track of size of a batch"""
93
+
94
+ mutations_count: int = 0
95
+ rows_count: int = 0
96
+ mutations_size: int = 0
97
+
98
+
99
+ class _FlowControl(object):
100
+ def __init__(
101
+ self,
102
+ max_mutations=MAX_OUTSTANDING_ELEMENTS,
103
+ max_mutation_bytes=MAX_OUTSTANDING_BYTES,
104
+ ):
105
+ """Control the inflight requests. Keep track of the mutations, row bytes and row counts.
106
+ As requests to backend are being made, adjust the number of mutations being processed.
107
+
108
+ If threshold is reached, block the flow.
109
+ Reopen the flow as requests are finished.
110
+ """
111
+ self.max_mutations = max_mutations
112
+ self.max_mutation_bytes = max_mutation_bytes
113
+ self.inflight_mutations = 0
114
+ self.inflight_size = 0
115
+ self.event = threading.Event()
116
+ self.event.set()
117
+ self._lock = threading.Lock()
118
+
119
+ def is_blocked(self):
120
+ """Returns True if:
121
+
122
+ - inflight mutations >= max_mutations, or
123
+ - inflight bytes size >= max_mutation_bytes, or
124
+ """
125
+
126
+ return (
127
+ self.inflight_mutations >= self.max_mutations
128
+ or self.inflight_size >= self.max_mutation_bytes
129
+ )
130
+
131
+ def control_flow(self, batch_info):
132
+ """
133
+ Calculate the resources used by this batch
134
+ """
135
+
136
+ with self._lock:
137
+ self.inflight_mutations += batch_info.mutations_count
138
+ self.inflight_size += batch_info.mutations_size
139
+ self.set_flow_control_status()
140
+
141
+ def wait(self):
142
+ """
143
+ Wait until flow control pushback has been released.
144
+ It awakens as soon as `event` is set.
145
+ """
146
+ self.event.wait()
147
+
148
+ def set_flow_control_status(self):
149
+ """Check the inflight mutations and size.
150
+
151
+ If values exceed the allowed threshold, block the event.
152
+ """
153
+ if self.is_blocked():
154
+ self.event.clear() # sleep
155
+ else:
156
+ self.event.set() # awaken the threads
157
+
158
+ def release(self, batch_info):
159
+ """
160
+ Release the resources.
161
+ Decrement the row size to allow enqueued mutations to be run.
162
+ """
163
+ with self._lock:
164
+ self.inflight_mutations -= batch_info.mutations_count
165
+ self.inflight_size -= batch_info.mutations_size
166
+ self.set_flow_control_status()
167
+
168
+
169
+ class MutationsBatcher(object):
170
+ """A MutationsBatcher is used in batch cases where the number of mutations
171
+ is large or unknown. It will store :class:`DirectRow` in memory until one of the
172
+ size limits is reached, or an explicit call to :func:`flush()` is performed. When
173
+ a flush event occurs, the :class:`DirectRow` in memory will be sent to Cloud
174
+ Bigtable. Batching mutations is more efficient than sending individual
175
+ request.
176
+
177
+ This class is not suited for usage in systems where each mutation
178
+ must be guaranteed to be sent, since calling mutate may only result in an
179
+ in-memory change. In a case of a system crash, any :class:`DirectRow` remaining in
180
+ memory will not necessarily be sent to the service, even after the
181
+ completion of the :func:`mutate()` method.
182
+
183
+ Note on thread safety: The same :class:`MutationBatcher` cannot be shared by multiple end-user threads.
184
+
185
+ :type table: class
186
+ :param table: class:`~google.cloud.bigtable.table.Table`.
187
+
188
+ :type flush_count: int
189
+ :param flush_count: (Optional) Max number of rows to flush. If it
190
+ reaches the max number of rows it calls finish_batch() to mutate the
191
+ current row batch. Default is FLUSH_COUNT (1000 rows).
192
+
193
+ :type max_row_bytes: int
194
+ :param max_row_bytes: (Optional) Max number of row mutations size to
195
+ flush. If it reaches the max number of row mutations size it calls
196
+ finish_batch() to mutate the current row batch. Default is MAX_ROW_BYTES
197
+ (5 MB).
198
+
199
+ :type flush_interval: float
200
+ :param flush_interval: (Optional) The interval (in seconds) between asynchronous flush.
201
+ Default is 1 second.
202
+
203
+ :type batch_completed_callback: Callable[list:[`~google.rpc.status_pb2.Status`]] = None
204
+ :param batch_completed_callback: (Optional) A callable for handling responses
205
+ after the current batch is sent. The callable function expect a list of grpc
206
+ Status.
207
+ """
208
+
209
+ def __init__(
210
+ self,
211
+ table,
212
+ flush_count=FLUSH_COUNT,
213
+ max_row_bytes=MAX_MUTATION_SIZE,
214
+ flush_interval=1,
215
+ batch_completed_callback=None,
216
+ ):
217
+ self._rows = _MutationsBatchQueue(
218
+ max_mutation_bytes=max_row_bytes, flush_count=flush_count
219
+ )
220
+ self.table = table
221
+ self._executor = concurrent.futures.ThreadPoolExecutor()
222
+ atexit.register(self.close)
223
+ self._timer = threading.Timer(flush_interval, self.flush)
224
+ self._timer.start()
225
+ self.flow_control = _FlowControl(
226
+ max_mutations=MAX_OUTSTANDING_ELEMENTS,
227
+ max_mutation_bytes=MAX_OUTSTANDING_BYTES,
228
+ )
229
+ self.futures_mapping = {}
230
+ self.exceptions = queue.Queue()
231
+ self._user_batch_completed_callback = batch_completed_callback
232
+
233
+ @property
234
+ def flush_count(self):
235
+ return self._rows.flush_count
236
+
237
+ @property
238
+ def max_row_bytes(self):
239
+ return self._rows.max_mutation_bytes
240
+
241
+ def __enter__(self):
242
+ """Starting the MutationsBatcher as a context manager"""
243
+ return self
244
+
245
+ def mutate(self, row):
246
+ """Add a row to the batch. If the current batch meets one of the size
247
+ limits, the batch is sent asynchronously.
248
+
249
+ For example:
250
+
251
+ .. literalinclude:: snippets_table.py
252
+ :start-after: [START bigtable_api_batcher_mutate]
253
+ :end-before: [END bigtable_api_batcher_mutate]
254
+ :dedent: 4
255
+
256
+ :type row: class
257
+ :param row: :class:`~google.cloud.bigtable.row.DirectRow`.
258
+
259
+ :raises: One of the following:
260
+ * :exc:`~.table._BigtableRetryableError` if any row returned a transient error.
261
+ * :exc:`RuntimeError` if the number of responses doesn't match the number of rows that were retried
262
+ """
263
+ self._rows.put(row)
264
+
265
+ if self._rows.full():
266
+ self._flush_async()
267
+
268
+ def mutate_rows(self, rows):
269
+ """Add multiple rows to the batch. If the current batch meets one of the size
270
+ limits, the batch is sent asynchronously.
271
+
272
+ For example:
273
+
274
+ .. literalinclude:: snippets_table.py
275
+ :start-after: [START bigtable_api_batcher_mutate_rows]
276
+ :end-before: [END bigtable_api_batcher_mutate_rows]
277
+ :dedent: 4
278
+
279
+ :type rows: list:[`~google.cloud.bigtable.row.DirectRow`]
280
+ :param rows: list:[`~google.cloud.bigtable.row.DirectRow`].
281
+
282
+ :raises: One of the following:
283
+ * :exc:`~.table._BigtableRetryableError` if any row returned a transient error.
284
+ * :exc:`RuntimeError` if the number of responses doesn't match the number of rows that were retried
285
+ """
286
+ for row in rows:
287
+ self.mutate(row)
288
+
289
+ def flush(self):
290
+ """Sends the current batch to Cloud Bigtable synchronously.
291
+ For example:
292
+
293
+ .. literalinclude:: snippets_table.py
294
+ :start-after: [START bigtable_api_batcher_flush]
295
+ :end-before: [END bigtable_api_batcher_flush]
296
+ :dedent: 4
297
+
298
+ :raises:
299
+ * :exc:`.batcherMutationsBatchError` if there's any error in the mutations.
300
+ """
301
+ rows_to_flush = []
302
+ row = self._rows.get()
303
+ while row is not None:
304
+ rows_to_flush.append(row)
305
+ row = self._rows.get()
306
+ response = self._flush_rows(rows_to_flush)
307
+ return response
308
+
309
+ def _flush_async(self):
310
+ """Sends the current batch to Cloud Bigtable asynchronously.
311
+
312
+ :raises:
313
+ * :exc:`.batcherMutationsBatchError` if there's any error in the mutations.
314
+ """
315
+ next_row = self._rows.get()
316
+ while next_row is not None:
317
+ # start a new batch
318
+ rows_to_flush = [next_row]
319
+ batch_info = _BatchInfo(
320
+ mutations_count=len(next_row._get_mutations()),
321
+ rows_count=1,
322
+ mutations_size=next_row.get_mutations_size(),
323
+ )
324
+ # fill up batch with rows
325
+ next_row = self._rows.get()
326
+ while next_row is not None and self._row_fits_in_batch(
327
+ next_row, batch_info
328
+ ):
329
+ rows_to_flush.append(next_row)
330
+ batch_info.mutations_count += len(next_row._get_mutations())
331
+ batch_info.rows_count += 1
332
+ batch_info.mutations_size += next_row.get_mutations_size()
333
+ next_row = self._rows.get()
334
+ # send batch over network
335
+ # wait for resources to become available
336
+ self.flow_control.wait()
337
+ # once unblocked, submit the batch
338
+ # event flag will be set by control_flow to block subsequent thread, but not blocking this one
339
+ self.flow_control.control_flow(batch_info)
340
+ future = self._executor.submit(self._flush_rows, rows_to_flush)
341
+ # schedule release of resources from flow control
342
+ self.futures_mapping[future] = batch_info
343
+ future.add_done_callback(self._batch_completed_callback)
344
+
345
+ def _batch_completed_callback(self, future):
346
+ """Callback for when the mutation has finished to clean up the current batch
347
+ and release items from the flow controller.
348
+ Raise exceptions if there's any.
349
+ Release the resources locked by the flow control and allow enqueued tasks to be run.
350
+ """
351
+ processed_rows = self.futures_mapping[future]
352
+ self.flow_control.release(processed_rows)
353
+ del self.futures_mapping[future]
354
+
355
+ def _row_fits_in_batch(self, row, batch_info):
356
+ """Checks if a row can fit in the current batch.
357
+
358
+ :type row: class
359
+ :param row: :class:`~google.cloud.bigtable.row.DirectRow`.
360
+
361
+ :type batch_info: :class:`_BatchInfo`
362
+ :param batch_info: Information about the current batch.
363
+
364
+ :rtype: bool
365
+ :returns: True if the row can fit in the current batch.
366
+ """
367
+ new_rows_count = batch_info.rows_count + 1
368
+ new_mutations_count = batch_info.mutations_count + len(row._get_mutations())
369
+ new_mutations_size = batch_info.mutations_size + row.get_mutations_size()
370
+ return (
371
+ new_rows_count <= self.flush_count
372
+ and new_mutations_size <= self.max_row_bytes
373
+ and new_mutations_count <= self.flow_control.max_mutations
374
+ and new_mutations_size <= self.flow_control.max_mutation_bytes
375
+ )
376
+
377
+ def _flush_rows(self, rows_to_flush):
378
+ """Mutate the specified rows.
379
+
380
+ :raises:
381
+ * :exc:`.batcherMutationsBatchError` if there's any error in the mutations.
382
+ """
383
+ responses = []
384
+ if len(rows_to_flush) > 0:
385
+ response = self.table.mutate_rows(rows_to_flush)
386
+
387
+ if self._user_batch_completed_callback:
388
+ self._user_batch_completed_callback(response)
389
+
390
+ for result in response:
391
+ if result.code != 0:
392
+ exc = from_grpc_status(result.code, result.message)
393
+ self.exceptions.put(exc)
394
+ responses.append(result)
395
+
396
+ return responses
397
+
398
+ def __exit__(self, exc_type, exc_value, exc_traceback):
399
+ """Clean up resources. Flush and shutdown the ThreadPoolExecutor."""
400
+ self.close()
401
+
402
+ def close(self):
403
+ """Clean up resources. Flush and shutdown the ThreadPoolExecutor.
404
+ Any errors will be raised.
405
+
406
+ :raises:
407
+ * :exc:`.batcherMutationsBatchError` if there's any error in the mutations.
408
+ """
409
+ self.flush()
410
+ self._executor.shutdown(wait=True)
411
+ atexit.unregister(self.close)
412
+ if self.exceptions.qsize() > 0:
413
+ exc = list(self.exceptions.queue)
414
+ raise MutationsBatchError("Errors in batch mutations.", exc=exc)