Skip to content

Commit 7f1b786

Browse files
committed
Using protobuf CommitRequest in datastore Connection.commit.
This is towards #1288 in preparation for the upgrade to `v1beta3`. In particular, a single `Mutation` protobuf instance in `v1beta3` is not sufficient to contain all changes to be committed, so we use the container that is up one level in the hierarchy.
1 parent 02c645f commit 7f1b786

File tree

6 files changed

+53
-44
lines changed

6 files changed

+53
-44
lines changed

gcloud/datastore/batch.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,12 @@ class Batch(object):
6060
:type client: :class:`gcloud.datastore.client.Client`
6161
:param client: The client used to connect to datastore.
6262
"""
63+
6364
_id = None # "protected" attribute, always None for non-transactions
6465

6566
def __init__(self, client):
6667
self._client = client
67-
self._mutation = _datastore_pb2.Mutation()
68+
self._commit_request = _datastore_pb2.CommitRequest()
6869
self._partial_key_entities = []
6970

7071
def current(self):
@@ -114,6 +115,9 @@ def _add_complete_key_entity_pb(self):
114115
:returns: The newly created entity protobuf that will be
115116
updated and sent with a commit.
116117
"""
118+
# We use ``upsert`` for entities with completed keys, rather than
119+
# ``insert`` or ``update``, in order not to create race conditions
120+
# based on prior existence / removal of the entity.
117121
return self.mutations.upsert.add()
118122

119123
def _add_delete_key_pb(self):
@@ -129,17 +133,16 @@ def _add_delete_key_pb(self):
129133
def mutations(self):
130134
"""Getter for the changes accumulated by this batch.
131135
132-
Every batch is committed with a single Mutation
133-
representing the 'work' to be done as part of the batch.
134-
Inside a batch, calling :meth:`put` with an entity, or
135-
:meth:`delete` with a key, builds up the mutation.
136-
This getter returns the Mutation protobuf that
137-
has been built-up so far.
136+
Every batch is committed with a single commit request containing all
137+
the work to be done as mutations. Inside a batch, calling :meth:`put`
138+
with an entity, or :meth:`delete` with a key, builds up the request by
139+
adding a new mutation. This getter returns the protobuf that has been
140+
built-up so far.
138141
139142
:rtype: :class:`gcloud.datastore._generated.datastore_pb2.Mutation`
140143
:returns: The Mutation protobuf to be sent in the commit request.
141144
"""
142-
return self._mutation
145+
return self._commit_request.mutation
143146

144147
def put(self, entity):
145148
"""Remember an entity's state to be saved during :meth:`commit`.
@@ -156,8 +159,8 @@ def put(self, entity):
156159
"bytes" ('str' in Python2, 'bytes' in Python3) map to 'blob_value'.
157160
158161
When an entity has a partial key, calling :meth:`commit` sends it as
159-
an ``insert_auto_id`` mutation and the key is completed. On return, the
160-
key for the ``entity`` passed in as updated to match the key ID
162+
an ``insert_auto_id`` mutation and the key is completed. On return,
163+
the key for the ``entity`` passed in is updated to match the key ID
161164
assigned by the server.
162165
163166
:type entity: :class:`gcloud.datastore.entity.Entity`
@@ -212,11 +215,10 @@ def commit(self):
212215
context manager.
213216
"""
214217
_, updated_keys = self.connection.commit(
215-
self.dataset_id, self.mutations, self._id)
218+
self.dataset_id, self._commit_request, self._id)
216219
# If the back-end returns without error, we are guaranteed that
217-
# the response's 'insert_auto_id_key' will match (length and order)
218-
# the request's 'insert_auto_id` entities, which are derived from
219-
# our '_partial_key_entities' (no partial success).
220+
# :meth:`Connection.commit` will return keys that match (length and
221+
# order) directly ``_partial_key_entities``.
220222
for new_key_pb, entity in zip(updated_keys,
221223
self._partial_key_entities):
222224
new_id = new_key_pb.path_element[-1].id

gcloud/datastore/connection.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -293,16 +293,16 @@ def begin_transaction(self, dataset_id):
293293
_datastore_pb2.BeginTransactionResponse)
294294
return response.transaction
295295

296-
def commit(self, dataset_id, mutation_pb, transaction_id):
297-
"""Commit dataset mutations in context of current transation (if any).
296+
def commit(self, dataset_id, commit_request, transaction_id):
297+
"""Commit mutations in context of current transation (if any).
298298
299299
Maps the ``DatastoreService.Commit`` protobuf RPC.
300300
301301
:type dataset_id: string
302302
:param dataset_id: The ID dataset to which the transaction applies.
303303
304-
:type mutation_pb: :class:`._generated.datastore_pb2.Mutation`
305-
:param mutation_pb: The protobuf for the mutations being saved.
304+
:type commit_request: :class:`._generated.datastore_pb2.CommitRequest`
305+
:param commit_request: The protobuf with the mutations being committed.
306306
307307
:type transaction_id: string or None
308308
:param transaction_id: The transaction ID returned from
@@ -315,14 +315,14 @@ def commit(self, dataset_id, mutation_pb, transaction_id):
315315
that was completed in the commit.
316316
"""
317317
request = _datastore_pb2.CommitRequest()
318+
request.CopyFrom(commit_request)
318319

319320
if transaction_id:
320321
request.mode = _datastore_pb2.CommitRequest.TRANSACTIONAL
321322
request.transaction = transaction_id
322323
else:
323324
request.mode = _datastore_pb2.CommitRequest.NON_TRANSACTIONAL
324325

325-
request.mutation.CopyFrom(mutation_pb)
326326
response = self._rpc(dataset_id, 'commit', request,
327327
_datastore_pb2.CommitResponse)
328328
return _parse_commit_response(response)

gcloud/datastore/test_batch.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def test_commit(self):
209209
batch.commit()
210210

211211
self.assertEqual(connection._committed,
212-
[(_DATASET, batch.mutations, None)])
212+
[(_DATASET, batch._commit_request, None)])
213213

214214
def test_commit_w_partial_key_entities(self):
215215
_DATASET = 'DATASET'
@@ -225,7 +225,7 @@ def test_commit_w_partial_key_entities(self):
225225
batch.commit()
226226

227227
self.assertEqual(connection._committed,
228-
[(_DATASET, batch.mutations, None)])
228+
[(_DATASET, batch._commit_request, None)])
229229
self.assertFalse(entity.key.is_partial)
230230
self.assertEqual(entity.key._id, _NEW_ID)
231231

@@ -248,7 +248,7 @@ def test_as_context_mgr_wo_error(self):
248248
mutated_entity = _mutated_pb(self, batch.mutations, 'upsert')
249249
self.assertEqual(mutated_entity.key, key._key)
250250
self.assertEqual(connection._committed,
251-
[(_DATASET, batch.mutations, None)])
251+
[(_DATASET, batch._commit_request, None)])
252252

253253
def test_as_context_mgr_nested(self):
254254
_DATASET = 'DATASET'
@@ -280,8 +280,8 @@ def test_as_context_mgr_nested(self):
280280
self.assertEqual(mutated_entity2.key, key2._key)
281281

282282
self.assertEqual(connection._committed,
283-
[(_DATASET, batch2.mutations, None),
284-
(_DATASET, batch1.mutations, None)])
283+
[(_DATASET, batch2._commit_request, None),
284+
(_DATASET, batch1._commit_request, None)])
285285

286286
def test_as_context_mgr_w_error(self):
287287
_DATASET = 'DATASET'
@@ -329,8 +329,8 @@ def __init__(self, *new_keys):
329329
self._committed = []
330330
self._index_updates = 0
331331

332-
def commit(self, dataset_id, mutation, transaction_id):
333-
self._committed.append((dataset_id, mutation, transaction_id))
332+
def commit(self, dataset_id, commit_request, transaction_id):
333+
self._committed.append((dataset_id, commit_request, transaction_id))
334334
return self._index_updates, self._completed_keys
335335

336336

gcloud/datastore/test_client.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -625,9 +625,10 @@ def test_put_multi_no_batch_w_partial_key(self):
625625
self.assertTrue(result is None)
626626

627627
self.assertEqual(len(client.connection._commit_cw), 1)
628-
dataset_id, mutation, transaction_id = client.connection._commit_cw[0]
628+
(dataset_id,
629+
commit_req, transaction_id) = client.connection._commit_cw[0]
629630
self.assertEqual(dataset_id, self.DATASET_ID)
630-
inserts = list(mutation.insert_auto_id)
631+
inserts = list(commit_req.mutation.insert_auto_id)
631632
self.assertEqual(len(inserts), 1)
632633
self.assertEqual(inserts[0].key, key.to_protobuf())
633634

@@ -697,9 +698,10 @@ def test_delete_multi_no_batch(self):
697698
result = client.delete_multi([key])
698699
self.assertEqual(result, None)
699700
self.assertEqual(len(client.connection._commit_cw), 1)
700-
dataset_id, mutation, transaction_id = client.connection._commit_cw[0]
701+
(dataset_id,
702+
commit_req, transaction_id) = client.connection._commit_cw[0]
701703
self.assertEqual(dataset_id, self.DATASET_ID)
702-
self.assertEqual(list(mutation.delete), [key.to_protobuf()])
704+
self.assertEqual(list(commit_req.mutation.delete), [key.to_protobuf()])
703705
self.assertTrue(transaction_id is None)
704706

705707
def test_delete_multi_w_existing_batch(self):
@@ -1012,8 +1014,8 @@ def lookup(self, dataset_id, key_pbs, eventual=False, transaction_id=None):
10121014
results, missing, deferred = triple
10131015
return results, missing, deferred
10141016

1015-
def commit(self, dataset_id, mutation, transaction_id):
1016-
self._commit_cw.append((dataset_id, mutation, transaction_id))
1017+
def commit(self, dataset_id, commit_request, transaction_id):
1018+
self._commit_cw.append((dataset_id, commit_request, transaction_id))
10171019
response, self._commit = self._commit[0], self._commit[1:]
10181020
return self._index_updates, response
10191021

gcloud/datastore/test_connection.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,8 @@ def test_commit_wo_transaction(self):
675675
DATASET_ID = 'DATASET'
676676
key_pb = self._make_key_pb(DATASET_ID)
677677
rsp_pb = datastore_pb2.CommitResponse()
678-
mutation = datastore_pb2.Mutation()
678+
req_pb = datastore_pb2.CommitRequest()
679+
mutation = req_pb.mutation
679680
insert = mutation.upsert.add()
680681
insert.key.CopyFrom(key_pb)
681682
value_pb = _new_value_pb(insert, 'foo')
@@ -700,7 +701,7 @@ def mock_parse(response):
700701
return expected_result
701702

702703
with _Monkey(MUT, _parse_commit_response=mock_parse):
703-
result = conn.commit(DATASET_ID, mutation, None)
704+
result = conn.commit(DATASET_ID, req_pb, None)
704705

705706
self.assertTrue(result is expected_result)
706707
cw = http._called_with
@@ -722,7 +723,8 @@ def test_commit_w_transaction(self):
722723
DATASET_ID = 'DATASET'
723724
key_pb = self._make_key_pb(DATASET_ID)
724725
rsp_pb = datastore_pb2.CommitResponse()
725-
mutation = datastore_pb2.Mutation()
726+
req_pb = datastore_pb2.CommitRequest()
727+
mutation = req_pb.mutation
726728
insert = mutation.upsert.add()
727729
insert.key.CopyFrom(key_pb)
728730
value_pb = _new_value_pb(insert, 'foo')
@@ -747,7 +749,7 @@ def mock_parse(response):
747749
return expected_result
748750

749751
with _Monkey(MUT, _parse_commit_response=mock_parse):
750-
result = conn.commit(DATASET_ID, mutation, b'xact')
752+
result = conn.commit(DATASET_ID, req_pb, b'xact')
751753

752754
self.assertTrue(result is expected_result)
753755
cw = http._called_with

gcloud/datastore/test_transaction.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,11 @@ def test_commit_no_partial_keys(self):
102102
connection = _Connection(234)
103103
client = _Client(_DATASET, connection)
104104
xact = self._makeOne(client)
105-
xact._mutation = mutation = object()
105+
xact._commit_request = commit_request = object()
106106
xact.begin()
107107
xact.commit()
108-
self.assertEqual(connection._committed, (_DATASET, mutation, 234))
108+
self.assertEqual(connection._committed,
109+
(_DATASET, commit_request, 234))
109110
self.assertEqual(xact.id, None)
110111

111112
def test_commit_w_partial_keys(self):
@@ -118,10 +119,11 @@ def test_commit_w_partial_keys(self):
118119
xact = self._makeOne(client)
119120
entity = _Entity()
120121
xact.put(entity)
121-
xact._mutation = mutation = object()
122+
xact._commit_request = commit_request = object()
122123
xact.begin()
123124
xact.commit()
124-
self.assertEqual(connection._committed, (_DATASET, mutation, 234))
125+
self.assertEqual(connection._committed,
126+
(_DATASET, commit_request, 234))
125127
self.assertEqual(xact.id, None)
126128
self.assertEqual(entity.key.path, [{'kind': _KIND, 'id': _ID}])
127129

@@ -130,11 +132,12 @@ def test_context_manager_no_raise(self):
130132
connection = _Connection(234)
131133
client = _Client(_DATASET, connection)
132134
xact = self._makeOne(client)
133-
xact._mutation = mutation = object()
135+
xact._commit_request = commit_request = object()
134136
with xact:
135137
self.assertEqual(xact.id, 234)
136138
self.assertEqual(connection._begun, _DATASET)
137-
self.assertEqual(connection._committed, (_DATASET, mutation, 234))
139+
self.assertEqual(connection._committed,
140+
(_DATASET, commit_request, 234))
138141
self.assertEqual(xact.id, None)
139142

140143
def test_context_manager_w_raise(self):
@@ -186,8 +189,8 @@ def begin_transaction(self, dataset_id):
186189
def rollback(self, dataset_id, transaction_id):
187190
self._rolled_back = dataset_id, transaction_id
188191

189-
def commit(self, dataset_id, mutation, transaction_id):
190-
self._committed = (dataset_id, mutation, transaction_id)
192+
def commit(self, dataset_id, commit_request, transaction_id):
193+
self._committed = (dataset_id, commit_request, transaction_id)
191194
return self._index_updates, self._completed_keys
192195

193196

0 commit comments

Comments
 (0)