Chromium Code Reviews| Index: appengine/findit/model/versioned_model.py |
| diff --git a/appengine/findit/model/versioned_model.py b/appengine/findit/model/versioned_model.py |
| index f45a10ad3ddabe23fe969ef739c3e600b6ea82be..7a923a180ce8133a44d1f06559dcd7d3512cacfc 100644 |
| --- a/appengine/findit/model/versioned_model.py |
| +++ b/appengine/findit/model/versioned_model.py |
| @@ -31,16 +31,46 @@ class VersionedModel(ndb.Model): |
| """ |
| @property |
| + def root_string_id(self): |
|
stgao
2016/09/22 00:44:01
Why do we care whether the root id is a string or
stgao
2016/09/22 00:44:01
Also not make it a public property.
lijeffrey
2016/09/22 17:36:15
Done.
|
| + if not self.key: |
| + return None |
| + |
| + key_pairs = self.key.pairs() |
| + root_string_id = key_pairs[0][1] |
| + |
| + if len(key_pairs) > 1: |
| + # The key is expected to have format Key(root, string_id, kind, version) |
| + return root_string_id |
| + |
| + if isinstance(root_string_id, basestring): |
| + # The key has format Key(kind, string_id) |
| + return root_string_id |
| + |
| + # Otherwise, the key has format Key(root, version_number). |
| + return None |
| + |
| + @property |
| def version(self): |
| - return self.key.integer_id() if self.key else 0 |
| + if not self.key: |
| + return 0 |
| + |
| + key_pairs = self.key.pairs() |
| + if len(key_pairs) == 1: |
| + # Key(root, version_number) or Key(kind, string_id). The latter is used |
|
stgao
2016/09/22 00:44:01
This is the child entity of the root.
Per discuss
lijeffrey
2016/09/22 17:36:14
A slight change is actually needed:
When creating
stgao
2016/09/23 06:27:14
Should we fix the problem in the subclasses (MFA h
stgao
2016/09/23 16:35:53
Another option is to add a function here for insta
|
| + # only when an entity has been created but not yet written to ndb. |
| + return self.key.integer_id() or 0 |
| + |
| + # Key(root, string_id, type, version_number). |
| + return key_pairs[-1][-1] |
| @classmethod |
| - def GetVersion(cls, version=None): |
| + def GetVersion(cls, root_string_id=None, version=None): |
| """Returns a version of the entity, the latest if version=None.""" |
| assert not ndb.in_transaction() |
| - root_key = cls._GetRootKey() |
| + root_key = cls._GetRootKey(root_string_id) |
| root = root_key.get() |
| + |
| if not root or not root.current: |
| return None |
| @@ -53,20 +83,32 @@ class VersionedModel(ndb.Model): |
| return ndb.Key(cls, version, parent=root_key).get() |
| @classmethod |
| - def GetLatestVersionNumber(cls): |
| - root_entity = cls._GetRootKey().get() |
| + def GetLatestVersionNumber(cls, root_string_id=None): |
| + root_entity = cls._GetRootKey(root_string_id).get() |
| if not root_entity: |
| return -1 |
| return root_entity.current |
| - def Save(self): |
| - """Saves the current entity, but as a new version.""" |
| - root_key = self._GetRootKey() |
| + def Save(self, should_try_next_version_number=True): |
|
stgao
2016/09/22 00:44:01
name nit: too long. How about retry_on_conflict or
lijeffrey
2016/09/22 17:36:15
Done.
|
| + """Saves the current entity, but as a new version. |
| + |
| + Args: |
| + should_try_next_version_number: Boolean whether or not the next version |
|
stgao
2016/09/22 00:44:01
Format nit:
Args:
param (type): comment.
lijeffrey
2016/09/22 17:36:14
Done.
|
| + number should automatically be tried in case another transaction writes |
| + the entity first with the same proposed new version number. |
| + |
| + Returns: |
| + The key of the newly written version, and a boolean whether or not this |
| + call to Save() was responsible for creating it. |
| + """ |
| + root_string_id = self.root_string_id |
|
stgao
2016/09/22 00:44:01
nit: root_string_id is used only once.
lijeffrey
2016/09/22 17:36:14
Done.
|
| + root_key = self._GetRootKey(root_string_id) |
| root = root_key.get() or self._GetRootModel()(key=root_key) |
| def SaveData(): |
| if self.key.get(): |
| return False # The entity exists, should retry. |
| + |
| ndb.put_multi([self, root]) |
| return True |
| @@ -77,11 +119,17 @@ class VersionedModel(ndb.Model): |
| SetNewKey() |
| while True: |
| while self.key.get(): |
| - SetNewKey() |
| + if should_try_next_version_number: |
| + SetNewKey() |
| + else: |
| + # Another transaction had already written the proposed new version, so |
| + # return that version's key and False indicating this call to Save() |
| + # was not responsible for creating it. |
| + return self.key, False |
| try: |
| if ndb.transaction(SaveData, retries=0): |
| - return self.key |
| + return self.key, True |
| except ( |
| datastore_errors.InternalError, |
| datastore_errors.Timeout, |
| @@ -95,7 +143,13 @@ class VersionedModel(ndb.Model): |
| RuntimeError) as e: |
| logging.info('Transaction failure: %s', e) |
| else: |
| - SetNewKey() |
| + if should_try_next_version_number: |
| + SetNewKey() |
| + else: |
| + # Another transaction had already written the proposed new version, so |
| + # return that version's key and False indicating this call to Save() |
| + # was not responsible for creating it. |
| + return self.key, False |
|
chanli
2016/09/22 00:11:25
Based on https://docs.python.org/3/tutorial/errors
stgao
2016/09/22 00:44:01
There is a if in line #131. We return only when we
|
| @classmethod |
| def _GetRootModel(cls): |
| @@ -111,5 +165,6 @@ class VersionedModel(ndb.Model): |
| return _RootModel |
| @classmethod |
| - def _GetRootKey(cls): |
| - return ndb.Key(cls._GetRootModel(), 1) |
| + def _GetRootKey(cls, string_id=None): |
|
stgao
2016/09/22 00:44:01
name nit: root_id to be consistent.
lijeffrey
2016/09/22 17:36:15
Done.
|
| + return ndb.Key( |
| + cls._GetRootModel(), string_id if string_id is not None else 1) |