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.
- google/cloud/bigtable/__init__.py +25 -0
- google/cloud/bigtable/app_profile.py +377 -0
- google/cloud/bigtable/backup.py +490 -0
- google/cloud/bigtable/batcher.py +414 -0
- google/cloud/bigtable/client.py +475 -0
- google/cloud/bigtable/cluster.py +541 -0
- google/cloud/bigtable/column_family.py +362 -0
- google/cloud/bigtable/data/__init__.py +103 -0
- google/cloud/bigtable/data/_async/__init__.py +25 -0
- google/cloud/bigtable/data/_async/_mutate_rows.py +226 -0
- google/cloud/bigtable/data/_async/_read_rows.py +363 -0
- google/cloud/bigtable/data/_async/client.py +1522 -0
- google/cloud/bigtable/data/_async/mutations_batcher.py +534 -0
- google/cloud/bigtable/data/_cross_sync/__init__.py +20 -0
- google/cloud/bigtable/data/_cross_sync/_decorators.py +441 -0
- google/cloud/bigtable/data/_cross_sync/_mapping_meta.py +64 -0
- google/cloud/bigtable/data/_cross_sync/cross_sync.py +334 -0
- google/cloud/bigtable/data/_helpers.py +249 -0
- google/cloud/bigtable/data/_sync_autogen/_mutate_rows.py +182 -0
- google/cloud/bigtable/data/_sync_autogen/_read_rows.py +304 -0
- google/cloud/bigtable/data/_sync_autogen/client.py +1293 -0
- google/cloud/bigtable/data/_sync_autogen/mutations_batcher.py +449 -0
- google/cloud/bigtable/data/exceptions.py +340 -0
- google/cloud/bigtable/data/execute_query/__init__.py +43 -0
- google/cloud/bigtable/data/execute_query/_async/__init__.py +13 -0
- google/cloud/bigtable/data/execute_query/_async/execute_query_iterator.py +291 -0
- google/cloud/bigtable/data/execute_query/_byte_cursor.py +123 -0
- google/cloud/bigtable/data/execute_query/_checksum.py +43 -0
- google/cloud/bigtable/data/execute_query/_parameters_formatting.py +155 -0
- google/cloud/bigtable/data/execute_query/_query_result_parsing_utils.py +134 -0
- google/cloud/bigtable/data/execute_query/_reader.py +122 -0
- google/cloud/bigtable/data/execute_query/_sync_autogen/execute_query_iterator.py +239 -0
- google/cloud/bigtable/data/execute_query/metadata.py +399 -0
- google/cloud/bigtable/data/execute_query/values.py +123 -0
- google/cloud/bigtable/data/mutations.py +380 -0
- google/cloud/bigtable/data/read_modify_write_rules.py +112 -0
- google/cloud/bigtable/data/read_rows_query.py +536 -0
- google/cloud/bigtable/data/row.py +535 -0
- google/cloud/bigtable/data/row_filters.py +968 -0
- google/cloud/bigtable/encryption_info.py +64 -0
- google/cloud/bigtable/enums.py +223 -0
- google/cloud/bigtable/error.py +64 -0
- google/cloud/bigtable/gapic_version.py +16 -0
- google/cloud/bigtable/helpers.py +31 -0
- google/cloud/bigtable/instance.py +789 -0
- google/cloud/bigtable/policy.py +255 -0
- google/cloud/bigtable/row.py +1267 -0
- google/cloud/bigtable/row_data.py +380 -0
- google/cloud/bigtable/row_filters.py +838 -0
- google/cloud/bigtable/row_merger.py +250 -0
- google/cloud/bigtable/row_set.py +213 -0
- google/cloud/bigtable/table.py +1409 -0
- google/cloud/bigtable_admin/__init__.py +410 -0
- google/cloud/bigtable_admin/gapic_version.py +16 -0
- google/cloud/bigtable_admin/py.typed +2 -0
- google/cloud/bigtable_admin_v2/__init__.py +250 -0
- google/cloud/bigtable_admin_v2/gapic_metadata.json +962 -0
- google/cloud/bigtable_admin_v2/gapic_version.py +16 -0
- google/cloud/bigtable_admin_v2/py.typed +2 -0
- google/cloud/bigtable_admin_v2/services/__init__.py +15 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/__init__.py +22 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/async_client.py +3465 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py +3944 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/pagers.py +681 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/__init__.py +40 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py +751 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py +1249 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py +1579 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest.py +6820 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest_base.py +1746 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/__init__.py +22 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py +3472 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py +3949 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/pagers.py +669 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/__init__.py +40 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py +708 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py +1257 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py +1549 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest.py +6609 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest_base.py +1714 -0
- google/cloud/bigtable_admin_v2/types/__init__.py +248 -0
- google/cloud/bigtable_admin_v2/types/bigtable_instance_admin.py +1364 -0
- google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py +1715 -0
- google/cloud/bigtable_admin_v2/types/common.py +81 -0
- google/cloud/bigtable_admin_v2/types/instance.py +819 -0
- google/cloud/bigtable_admin_v2/types/table.py +1028 -0
- google/cloud/bigtable_admin_v2/types/types.py +776 -0
- google/cloud/bigtable_v2/__init__.py +136 -0
- google/cloud/bigtable_v2/gapic_metadata.json +193 -0
- google/cloud/bigtable_v2/gapic_version.py +16 -0
- google/cloud/bigtable_v2/py.typed +2 -0
- google/cloud/bigtable_v2/services/__init__.py +15 -0
- google/cloud/bigtable_v2/services/bigtable/__init__.py +22 -0
- google/cloud/bigtable_v2/services/bigtable/async_client.py +1741 -0
- google/cloud/bigtable_v2/services/bigtable/client.py +2194 -0
- google/cloud/bigtable_v2/services/bigtable/transports/__init__.py +38 -0
- google/cloud/bigtable_v2/services/bigtable/transports/base.py +343 -0
- google/cloud/bigtable_v2/services/bigtable/transports/grpc.py +660 -0
- google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py +762 -0
- google/cloud/bigtable_v2/services/bigtable/transports/rest.py +2489 -0
- google/cloud/bigtable_v2/services/bigtable/transports/rest_base.py +711 -0
- google/cloud/bigtable_v2/types/__init__.py +138 -0
- google/cloud/bigtable_v2/types/bigtable.py +1531 -0
- google/cloud/bigtable_v2/types/data.py +1612 -0
- google/cloud/bigtable_v2/types/feature_flags.py +119 -0
- google/cloud/bigtable_v2/types/request_stats.py +171 -0
- google/cloud/bigtable_v2/types/response_params.py +64 -0
- google/cloud/bigtable_v2/types/types.py +579 -0
- google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_admin_v2_keywords.py +233 -0
- google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_v2_keywords.py +186 -0
- google_cloud_bigtable-2.30.0.dist-info/LICENSE +202 -0
- google_cloud_bigtable-2.30.0.dist-info/METADATA +154 -0
- google_cloud_bigtable-2.30.0.dist-info/RECORD +115 -0
- google_cloud_bigtable-2.30.0.dist-info/WHEEL +5 -0
- 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)
|