Skip to content

Commit 990f86e

Browse files
feat(bigtable): Rerouted CheckAndMutateRows and ReadModifyWriteRows to data client (#18190)
Migrating over @gkevinzheng PR from bigtable monorepo googleapis/python-bigtable#1257 ### Original description: > **Changes made:** > > - `Row` objects hold `Mutation` and `ReadModifyWriteRowRule` objects from the data client rather than raw protos. > - Rerouted `ConditionalRow.commit` and `AppendRow.commit` (CheckAndMutateRows and ReadModifyWriteRows respectively) to use the data client, or more specifically, `self._table._table_impl` > - Added function `DirectRow._to_mutation_pbs` for retrieving mutations in proto form for the current `MutateRows` implementation, as well as for `DirectRow.get_mutations_size`. > - Removed unnecessary helper functions and tests for helper functions Note to reviewers: This PR has already been reviewed and merged to a staging branch, with the intention of doing a single merge to main. We are now planning to slowly rollout these changes back to the main branch. Minimal re-review should be necessary --------- Co-authored-by: Kevin Zheng <147537668+gkevinzheng@users.noreply.github.com>
1 parent 5ecfe0a commit 990f86e

5 files changed

Lines changed: 169 additions & 301 deletions

File tree

packages/google-cloud-bigtable/google/cloud/bigtable/row.py

Lines changed: 56 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,14 @@
1414

1515
"""User-friendly container for Google Cloud Bigtable Row."""
1616

17-
import struct
18-
1917
from google.cloud._helpers import (
2018
_datetime_from_microseconds, # type: ignore
2119
_microseconds_from_datetime, # type: ignore
2220
_to_bytes, # type: ignore
2321
)
2422

25-
from google.cloud.bigtable_v2.types import data as data_v2_pb2
26-
27-
_PACK_I64 = struct.Struct(">q").pack
23+
from google.cloud.bigtable.data import mutations
24+
from google.cloud.bigtable.data import read_modify_write_rules as rmw_rules
2825

2926
MAX_MUTATIONS = 100000
3027
"""The maximum number of mutations that a row can accumulate."""
@@ -158,26 +155,21 @@ def _set_cell(self, column_family_id, column, value, timestamp=None, state=None)
158155
:param state: (Optional) The state that is passed along to
159156
:meth:`_get_mutations`.
160157
"""
161-
column = _to_bytes(column)
162-
if isinstance(value, int):
163-
value = _PACK_I64(value)
164-
value = _to_bytes(value)
165158
if timestamp is None:
166-
# Use -1 for current Bigtable server time.
167-
timestamp_micros = -1
159+
# Use current Bigtable server time.
160+
timestamp_micros = mutations._SERVER_SIDE_TIMESTAMP
168161
else:
169162
timestamp_micros = _microseconds_from_datetime(timestamp)
170163
# Truncate to millisecond granularity.
171164
timestamp_micros -= timestamp_micros % 1000
172165

173-
mutation_val = data_v2_pb2.Mutation.SetCell(
174-
family_name=column_family_id,
175-
column_qualifier=column,
166+
mutation = mutations.SetCell(
167+
family=column_family_id,
168+
qualifier=column,
169+
new_value=value,
176170
timestamp_micros=timestamp_micros,
177-
value=value,
178171
)
179-
mutation_pb = data_v2_pb2.Mutation(set_cell=mutation_val)
180-
self._get_mutations(state).append(mutation_pb)
172+
self._get_mutations(state).append(mutation)
181173

182174
def _delete(self, state=None):
183175
"""Helper for :meth:`delete`
@@ -192,9 +184,7 @@ def _delete(self, state=None):
192184
:param state: (Optional) The state that is passed along to
193185
:meth:`_get_mutations`.
194186
"""
195-
mutation_val = data_v2_pb2.Mutation.DeleteFromRow()
196-
mutation_pb = data_v2_pb2.Mutation(delete_from_row=mutation_val)
197-
self._get_mutations(state).append(mutation_pb)
187+
self._get_mutations(state).append(mutations.DeleteAllFromRow())
198188

199189
def _delete_cells(self, column_family_id, columns, time_range=None, state=None):
200190
"""Helper for :meth:`delete_cell` and :meth:`delete_cells`.
@@ -221,33 +211,30 @@ def _delete_cells(self, column_family_id, columns, time_range=None, state=None):
221211
:param state: (Optional) The state that is passed along to
222212
:meth:`_get_mutations`.
223213
"""
224-
mutations_list = self._get_mutations(state)
225214
if columns is self.ALL_COLUMNS:
226-
mutation_val = data_v2_pb2.Mutation.DeleteFromFamily(
227-
family_name=column_family_id
215+
self._get_mutations(state).append(
216+
mutations.DeleteAllFromFamily(family_to_delete=column_family_id)
228217
)
229-
mutation_pb = data_v2_pb2.Mutation(delete_from_family=mutation_val)
230-
mutations_list.append(mutation_pb)
231218
else:
232-
delete_kwargs = {}
233-
if time_range is not None:
234-
delete_kwargs["time_range"] = time_range._to_pb()
219+
timestamps = time_range._to_dict() if time_range else {}
220+
start_timestamp_micros = timestamps.get("start_timestamp_micros")
221+
end_timestamp_micros = timestamps.get("end_timestamp_micros")
235222

236223
to_append = []
237224
for column in columns:
238225
column = _to_bytes(column)
239-
# time_range will never change if present, but the rest of
240-
# delete_kwargs will
241-
delete_kwargs.update(
242-
family_name=column_family_id, column_qualifier=column
226+
to_append.append(
227+
mutations.DeleteRangeFromColumn(
228+
family=column_family_id,
229+
qualifier=column,
230+
start_timestamp_micros=start_timestamp_micros,
231+
end_timestamp_micros=end_timestamp_micros,
232+
)
243233
)
244-
mutation_val = data_v2_pb2.Mutation.DeleteFromColumn(**delete_kwargs)
245-
mutation_pb = data_v2_pb2.Mutation(delete_from_column=mutation_val)
246-
to_append.append(mutation_pb)
247234

248235
# We don't add the mutations until all columns have been
249236
# processed without error.
250-
mutations_list.extend(to_append)
237+
self._get_mutations(state).extend(to_append)
251238

252239

253240
class DirectRow(_SetDeleteRow):
@@ -285,7 +272,7 @@ class DirectRow(_SetDeleteRow):
285272

286273
def __init__(self, row_key, table=None):
287274
super(DirectRow, self).__init__(row_key, table)
288-
self._pb_mutations = []
275+
self._mutations = []
289276

290277
def _get_mutations(self, state=None): # pylint: disable=unused-argument
291278
"""Gets the list of mutations for a given state.
@@ -300,7 +287,12 @@ def _get_mutations(self, state=None): # pylint: disable=unused-argument
300287
:rtype: list
301288
:returns: The list to add new mutations to (for the current state).
302289
"""
303-
return self._pb_mutations
290+
return self._mutations
291+
292+
def _get_mutation_pbs(self):
293+
"""Gets the list of mutation protos."""
294+
295+
return [mut._to_pb() for mut in self._get_mutations()]
304296

305297
def get_mutations_size(self):
306298
"""Gets the total mutations size for current row
@@ -314,7 +306,7 @@ def get_mutations_size(self):
314306
"""
315307

316308
mutation_size = 0
317-
for mutation in self._get_mutations():
309+
for mutation in self._get_mutation_pbs():
318310
mutation_size += mutation._pb.ByteSize()
319311

320312
return mutation_size
@@ -487,7 +479,7 @@ def clear(self):
487479
:end-before: [END bigtable_api_row_clear]
488480
:dedent: 4
489481
"""
490-
del self._pb_mutations[:]
482+
del self._mutations[:]
491483

492484

493485
class ConditionalRow(_SetDeleteRow):
@@ -598,17 +590,15 @@ def commit(self):
598590
% (MAX_MUTATIONS, num_true_mutations, num_false_mutations)
599591
)
600592

601-
data_client = self._table._instance._client.table_data_client
602-
resp = data_client.check_and_mutate_row(
603-
table_name=self._table.name,
593+
table = self._table._table_impl
594+
resp = table.check_and_mutate_row(
604595
row_key=self._row_key,
605-
predicate_filter=self._filter._to_pb(),
606-
app_profile_id=self._table._app_profile_id,
607-
true_mutations=true_mutations,
608-
false_mutations=false_mutations,
596+
predicate=self._filter,
597+
true_case_mutations=true_mutations,
598+
false_case_mutations=false_mutations,
609599
)
610600
self.clear()
611-
return resp.predicate_matched
601+
return resp
612602

613603
# pylint: disable=arguments-differ
614604
def set_cell(self, column_family_id, column, value, timestamp=None, state=True):
@@ -798,7 +788,7 @@ class AppendRow(Row):
798788

799789
def __init__(self, row_key, table):
800790
super(AppendRow, self).__init__(row_key, table)
801-
self._rule_pb_list = []
791+
self._rule_list = []
802792

803793
def clear(self):
804794
"""Removes all currently accumulated modifications on current row.
@@ -810,7 +800,7 @@ def clear(self):
810800
:end-before: [END bigtable_api_row_clear]
811801
:dedent: 4
812802
"""
813-
del self._rule_pb_list[:]
803+
del self._rule_list[:]
814804

815805
def append_cell_value(self, column_family_id, column, value):
816806
"""Appends a value to an existing cell.
@@ -843,12 +833,11 @@ def append_cell_value(self, column_family_id, column, value):
843833
the targeted cell is unset, it will be treated as
844834
containing the empty string.
845835
"""
846-
column = _to_bytes(column)
847-
value = _to_bytes(value)
848-
rule_pb = data_v2_pb2.ReadModifyWriteRule(
849-
family_name=column_family_id, column_qualifier=column, append_value=value
836+
self._rule_list.append(
837+
rmw_rules.AppendValueRule(
838+
family=column_family_id, qualifier=column, append_value=value
839+
)
850840
)
851-
self._rule_pb_list.append(rule_pb)
852841

853842
def increment_cell_value(self, column_family_id, column, int_value):
854843
"""Increments a value in an existing cell.
@@ -887,13 +876,11 @@ def increment_cell_value(self, column_family_id, column, int_value):
887876
big-endian signed integer), or the entire request
888877
will fail.
889878
"""
890-
column = _to_bytes(column)
891-
rule_pb = data_v2_pb2.ReadModifyWriteRule(
892-
family_name=column_family_id,
893-
column_qualifier=column,
894-
increment_amount=int_value,
879+
self._rule_list.append(
880+
rmw_rules.IncrementRule(
881+
family=column_family_id, qualifier=column, increment_amount=int_value
882+
)
895883
)
896-
self._rule_pb_list.append(rule_pb)
897884

898885
def commit(self):
899886
"""Makes a ``ReadModifyWriteRow`` API request.
@@ -926,7 +913,7 @@ def commit(self):
926913
:raises: :class:`ValueError <exceptions.ValueError>` if the number of
927914
mutations exceeds the :data:`MAX_MUTATIONS`.
928915
"""
929-
num_mutations = len(self._rule_pb_list)
916+
num_mutations = len(self._rule_list)
930917
if num_mutations == 0:
931918
return {}
932919
if num_mutations > MAX_MUTATIONS:
@@ -935,12 +922,10 @@ def commit(self):
935922
"allowable %d." % (num_mutations, MAX_MUTATIONS)
936923
)
937924

938-
data_client = self._table._instance._client.table_data_client
939-
row_response = data_client.read_modify_write_row(
940-
table_name=self._table.name,
925+
table = self._table._table_impl
926+
row_response = table.read_modify_write_row(
941927
row_key=self._row_key,
942-
rules=self._rule_pb_list,
943-
app_profile_id=self._table._app_profile_id,
928+
rules=self._rule_list,
944929
)
945930

946931
# Reset modifications after commit-ing request.
@@ -984,47 +969,13 @@ def _parse_rmw_row_response(row_response):
984969
}
985970
"""
986971
result = {}
987-
for column_family in row_response.row.families:
988-
column_family_id, curr_family = _parse_family_pb(column_family)
989-
result[column_family_id] = curr_family
972+
for cell in row_response.cells:
973+
column_family = result.setdefault(cell.family, {})
974+
column = column_family.setdefault(cell.qualifier, [])
975+
column.append((cell.value, _datetime_from_microseconds(cell.timestamp_micros)))
990976
return result
991977

992978

993-
def _parse_family_pb(family_pb):
994-
"""Parses a Family protobuf into a dictionary.
995-
996-
:type family_pb: :class:`._generated.data_pb2.Family`
997-
:param family_pb: A protobuf
998-
999-
:rtype: tuple
1000-
:returns: A string and dictionary. The string is the name of the
1001-
column family and the dictionary has column names (within the
1002-
family) as keys and cell lists as values. Each cell is
1003-
represented with a two-tuple with the value (in bytes) and the
1004-
timestamp for the cell. For example:
1005-
1006-
.. code:: python
1007-
1008-
{
1009-
b'col-name1': [
1010-
(b'cell-val', datetime.datetime(...)),
1011-
(b'cell-val-newer', datetime.datetime(...)),
1012-
],
1013-
b'col-name2': [
1014-
(b'altcol-cell-val', datetime.datetime(...)),
1015-
],
1016-
}
1017-
"""
1018-
result = {}
1019-
for column in family_pb.columns:
1020-
result[column.qualifier] = cells = []
1021-
for cell in column.cells:
1022-
val_pair = (cell.value, _datetime_from_microseconds(cell.timestamp_micros))
1023-
cells.append(val_pair)
1024-
1025-
return family_pb.name, result
1026-
1027-
1028979
class PartialRowData(object):
1029980
"""Representation of partial row in a Google Cloud Bigtable Table.
1030981

packages/google-cloud-bigtable/google/cloud/bigtable/table.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1374,7 +1374,7 @@ def _compile_mutation_entries(table_name, rows):
13741374
for row in rows:
13751375
_check_row_table_name(table_name, row)
13761376
_check_row_type(row)
1377-
mutations = row._get_mutations()
1377+
mutations = row._get_mutation_pbs()
13781378
entries.append(entry_klass(row_key=row.row_key, mutations=mutations))
13791379
mutations_count += len(mutations)
13801380

0 commit comments

Comments
 (0)