Chromium Code Reviews| Index: appengine/cr-buildbucket/service.py | 
| diff --git a/appengine/cr-buildbucket/service.py b/appengine/cr-buildbucket/service.py | 
| index 0fcd66fd5548075644b44cd4ba1ea5c838f494ad..dfba32aad237ab8da72939e5568b647cdff79051 100644 | 
| --- a/appengine/cr-buildbucket/service.py | 
| +++ b/appengine/cr-buildbucket/service.py | 
| @@ -10,6 +10,7 @@ import urlparse | 
| from components import auth | 
| from components import utils | 
| +from google.appengine.api import memcache | 
| from google.appengine.api import taskqueue | 
| from google.appengine.ext import db | 
| from google.appengine.ext import ndb | 
| @@ -90,8 +91,10 @@ def current_identity_cannot(action_format, *args): | 
| class BuildBucketService(object): | 
| - def add( | 
| - self, bucket, tags=None, parameters=None, lease_expiration_date=None): | 
| + @ndb.tasklet | 
| + def add_async( | 
| + self, bucket, tags=None, parameters=None, lease_expiration_date=None, | 
| + client_operation_id=None): | 
| """Adds the build entity to the build bucket. | 
| Requires the current user to have permissions to add builds to the | 
| @@ -104,6 +107,9 @@ class BuildBucketService(object): | 
| build creation. | 
| lease_expiration_date (datetime.datetime): if not None, the build is | 
| created as leased and its lease_key is not None. | 
| + client_operation_id (str): client-supplied operation id. If an | 
| + existing build with the same client id exists, it will be returned | 
| + instead. | 
| 
 
Vadim Sh.
2015/04/16 00:12:21
mention that expires within 1 min
 
nodir
2015/04/16 00:36:20
Done.
 
 | 
| Returns: | 
| A new Build. | 
| @@ -118,6 +124,16 @@ class BuildBucketService(object): | 
| if not acl.can_add_build(bucket, identity): | 
| raise current_identity_cannot('add builds to bucket %s', bucket) | 
| + if client_operation_id is not None: | 
| + client_operation_cache_key = ( | 
| + 'client_op/%s/%s/add_build' % ( | 
| + identity.to_bytes(), client_operation_id)) | 
| 
 
Vadim Sh.
2015/04/16 00:12:21
forbid "/" in client_operation_id
or hash it befor
 
nodir
2015/04/16 00:36:20
Done.
 
 | 
| + build_id = memcache.get(client_operation_cache_key) | 
| + if build_id: | 
| + build = yield model.Build.get_by_id_async(build_id) | 
| + if build: # pragma: no branch | 
| + raise ndb.Return(build) | 
| + | 
| build = model.Build( | 
| id=model.new_build_id(), | 
| bucket=bucket, | 
| @@ -130,10 +146,17 @@ class BuildBucketService(object): | 
| build.lease_expiration_date = lease_expiration_date | 
| build.leasee = auth.get_current_identity() | 
| build.regenerate_lease_key() | 
| - build.put() | 
| + yield build.put_async() | 
| logging.info( | 
| 'Build %s was created by %s', build.key.id(), identity.to_bytes()) | 
| - return build | 
| + | 
| + if client_operation_id is not None: | 
| + memcache.set(client_operation_cache_key, build.key.id(), 60) | 
| 
 
Vadim Sh.
2015/04/16 00:12:21
do it async too, ndb supports memcache (forgot exa
 
nodir
2015/04/16 00:36:20
Done.
 
 | 
| + raise ndb.Return(build) | 
| + | 
| + def add(self, *args, **kwargs): | 
| + """Sync version of add_async.""" | 
| + return self.add_async(*args, **kwargs).get_result() | 
| def get(self, build_id): | 
| """Gets a build by |build_id|. |