-
Notifications
You must be signed in to change notification settings - Fork 61
feat: Rerouted DirectRow.commit to use MutateRow #1276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3_staging
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -14,13 +14,18 @@ | |||||
|
|
||||||
| """User-friendly container for Google Cloud Bigtable Row.""" | ||||||
|
|
||||||
| from google.api_core.exceptions import GoogleAPICallError | ||||||
|
|
||||||
| from google.cloud._helpers import _datetime_from_microseconds # type: ignore | ||||||
| from google.cloud._helpers import _microseconds_from_datetime # type: ignore | ||||||
| from google.cloud._helpers import _to_bytes # type: ignore | ||||||
|
|
||||||
| from google.cloud.bigtable.data import mutations | ||||||
| from google.cloud.bigtable.data import read_modify_write_rules as rmw_rules | ||||||
|
|
||||||
| from google.rpc import code_pb2 | ||||||
| from google.rpc import status_pb2 | ||||||
|
|
||||||
|
|
||||||
| MAX_MUTATIONS = 100000 | ||||||
| """The maximum number of mutations that a row can accumulate.""" | ||||||
|
|
@@ -459,14 +464,21 @@ def commit(self): | |||||
| :rtype: :class:`~google.rpc.status_pb2.Status` | ||||||
| :returns: A response status (`google.rpc.status_pb2.Status`) | ||||||
| representing success or failure of the row committed. | ||||||
| :raises: :exc:`~.table.TooManyMutationsError` if the number of | ||||||
| mutations is greater than 100,000. | ||||||
| """ | ||||||
| response = self._table.mutate_rows([self]) | ||||||
|
|
||||||
| self.clear() | ||||||
|
|
||||||
| return response[0] | ||||||
| try: | ||||||
| self._table._table_impl.mutate_row(self.row_key, self._get_mutations()) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mutate_row has automatic retries. Is that expected? Did the previous implementation retry?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The previous implementation used |
||||||
| return status_pb2.Status() | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Conisder this for clarity
Suggested change
|
||||||
| except GoogleAPICallError as e: | ||||||
| # If the RPC call returns an error, extract the error into a status object, if possible. | ||||||
| return status_pb2.Status( | ||||||
| code=e.grpc_status_code.value[0] | ||||||
| if e.grpc_status_code is not None | ||||||
| else code_pb2.UNKNOWN, | ||||||
| message=e.message, | ||||||
| details=e.details, | ||||||
| ) | ||||||
|
Comment on lines
+468
to
+479
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||||||
| finally: | ||||||
| self.clear() | ||||||
|
|
||||||
| def clear(self): | ||||||
| """Removes all currently accumulated mutations on the current row. | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -219,6 +219,37 @@ def test_table_read_rows_filter_millis(data_table): | |
| row_data.consume_all() | ||
|
|
||
|
|
||
| def test_table_direct_row_commit(data_table, rows_to_delete): | ||
| from google.rpc import code_pb2 | ||
|
|
||
| row = data_table.direct_row(ROW_KEY) | ||
|
|
||
| # Test set cell | ||
| row.set_cell(COLUMN_FAMILY_ID1, COL_NAME1, CELL_VAL1) | ||
| row.set_cell(COLUMN_FAMILY_ID1, COL_NAME2, CELL_VAL1) | ||
| status = row.commit() | ||
| rows_to_delete.append(row) | ||
| assert status.code == code_pb2.Code.OK | ||
| row_data = data_table.read_row(ROW_KEY) | ||
| assert row_data.cells[COLUMN_FAMILY_ID1][COL_NAME1][0].value == CELL_VAL1 | ||
| assert row_data.cells[COLUMN_FAMILY_ID1][COL_NAME2][0].value == CELL_VAL1 | ||
|
|
||
| # Test delete cell | ||
| row.delete_cell(COLUMN_FAMILY_ID1, COL_NAME1) | ||
| status = row.commit() | ||
| assert status.code == code_pb2.Code.OK | ||
| row_data = data_table.read_row(ROW_KEY) | ||
| assert COL_NAME1 not in row_data.cells[COLUMN_FAMILY_ID1] | ||
| assert row_data.cells[COLUMN_FAMILY_ID1][COL_NAME2][0].value == CELL_VAL1 | ||
|
|
||
| # Test delete row | ||
| row.delete() | ||
| status = row.commit() | ||
| assert status.code == code_pb2.Code.OK | ||
| row_data = data_table.read_row(ROW_KEY) | ||
| assert row_data is None | ||
|
|
||
|
|
||
| def test_table_mutate_rows(data_table, rows_to_delete): | ||
| row1 = data_table.direct_row(ROW_KEY) | ||
| row1.set_cell(COLUMN_FAMILY_ID1, COL_NAME1, CELL_VAL1) | ||
|
|
@@ -1028,7 +1059,6 @@ def test_table_sample_row_keys(data_table, skip_on_emulator): | |
|
|
||
|
|
||
| def test_table_direct_row_input_errors(data_table, rows_to_delete): | ||
| from google.api_core.exceptions import InvalidArgument | ||
| from google.cloud.bigtable.row import MAX_MUTATIONS | ||
|
|
||
| row = data_table.direct_row(ROW_KEY) | ||
|
|
@@ -1054,19 +1084,17 @@ def test_table_direct_row_input_errors(data_table, rows_to_delete): | |
| with pytest.raises(TypeError): | ||
| row.set_cell(COLUMN_FAMILY_ID1, COL_NAME1, FLOAT_CELL_VAL) | ||
|
|
||
| # Can't have more than MAX_MUTATIONS mutations, but only enforced after | ||
| # a row.commit | ||
| # Can't have more than MAX_MUTATIONS mutations, enforced on server side now. | ||
| row.clear() | ||
| for _ in range(0, MAX_MUTATIONS + 1): | ||
| row.set_cell(COLUMN_FAMILY_ID1, COL_NAME1, CELL_VAL1) | ||
|
|
||
| with pytest.raises(ValueError): | ||
| row.commit() | ||
| resp = row.commit() | ||
| assert resp.code == StatusCode.INVALID_ARGUMENT.value[0] | ||
|
|
||
| # Not having any mutations gives a server error (InvalidArgument), not | ||
| # enforced on the client side. | ||
| # Not having any mutations gives a ValueError enforced on the client side. | ||
| row.clear() | ||
| with pytest.raises(InvalidArgument): | ||
| with pytest.raises(ValueError): | ||
| row.commit() | ||
|
Comment on lines
+1095
to
1098
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment on line 1095 states, "Not having any mutations gives a ValueError enforced on the client side." However, the |
||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring says "If no mutations have been created in the row, no request is made.". But it looks like mutate_row raises a ValueError if no mutations are present.
You should probably catch and swallow that exception, and make sure this case is handled in tests