Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(854)

Side by Side Diff: appengine/findit/model/versioned_model.py

Issue 1499583002: [Findit] Add a model to provide versioning and also a verioned config model. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Provides a model to support versioned entities in datastore.
6
7 Idea: use a root model entity to keep track of the most recent version of a
8 versioned entity, and make the versioned entities and the root model entity in
9 the same entity group so that they could be read and written in a transaction.
10 """
11
12 import logging
13
14 from google.appengine.api import datastore_errors
15 from google.appengine.ext import ndb
16 from google.appengine.runtime import apiproxy_errors
17
18
19 class _GroupRoot(ndb.Model):
20 """Root entity of a group to support versioned children."""
21 # Key id of the most recent child entity in the datastore. It is monotonically
22 # increasing and is 0 if no child is present.
23 current = ndb.IntegerProperty(indexed=False, default=0)
24
25
26 class VersionedModel(ndb.Model):
27 """A model that supports versioning.
28
29 Subclass will automatically be versioned, if use GetMostRecentVersion() to
30 read and use Save() to write.
31 """
32
33 @property
34 def version(self):
35 return self.key.integer_id()
36
37 @classmethod
38 def GetMostRecentVersion(cls):
39 """Returns the most recent version of the entity."""
40 assert not ndb.in_transaction()
41
42 root_key = cls._GetRootKey()
43 root = root_key.get()
44 if not root or not root.current:
45 return None
46
47 return ndb.Key(cls, root.current, parent=root_key).get()
48
49 def Save(self):
50 """Saves the current entity, but as a new version."""
51 root_key = self._GetRootKey()
52 root = root_key.get() or self._GetRootModel()(key=root_key)
53
54 def SaveData():
55 if self.key.get():
56 return False # The entity exists, should retry.
57 ndb.put_multi([self, root])
58 return True
59
60 def SetNewKey():
61 root.current += 1
62 self.key = ndb.Key(self.__class__, root.current, parent=root_key)
63
64 SetNewKey()
65 while True:
lijeffrey1 2015/12/03 18:51:37 so is this while True used to perform a retry if a
stgao 2015/12/03 19:43:56 Yes, this is to retry for exception or key already
66 while self.key.get():
67 SetNewKey()
68
69 try:
70 if ndb.transaction(SaveData, retries=0):
71 return self.key
72 except (
73 datastore_errors.InternalError,
74 datastore_errors.Timeout,
75 datastore_errors.TransactionFailedError) as e:
76 # https://cloud.google.com/appengine/docs/python/datastore/transactions
77 # states the result is ambiguous, it could have succeeded.
78 logging.info('Transaction likely failed: %s', e)
79 except (
80 apiproxy_errors.CancelledError,
81 datastore_errors.BadRequestError,
82 RuntimeError) as e:
83 logging.info('Transaction failure: %s', e)
84 else:
85 SetNewKey()
86
87 @classmethod
88 def _GetRootModel(cls):
89 """Returns a root model that can be used for versioned entities."""
90 root_model_name = '%sRoot' % cls.__name__
91
92 class _RootModel(_GroupRoot):
93 @classmethod
94 def _get_kind(cls):
95 return root_model_name
96
97 return _RootModel
98
99 @classmethod
100 def _GetRootKey(cls):
101 return ndb.Key(cls._GetRootModel(), 1)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698