I'm working on an AppEngine project that requires flexible searches over a dynamic dataset, and Google's Search API seems to fit the bill, so I'm thinking of using it to persist the dataset. The one big flaw is that it has no ACID properties or concept of transactions, especially in dealing the concurrent changes to the Datastore. I think I can work around it by implementing locks stored in Memcache.
Something like this seems like it would work:
@ndb.transactional
def do_in_transaction(stuff):
client = memcache.Client()
# acquire lock
while True:
lock = client.gets(doc_id)
if lock == 'arbitrary payload':
time.sleep(.01)
elif client.cas(doc_id, lock='arbitrary payload')
break
try:
old_doc = index.get(doc_id)
...do stuff in the datastore...
...push new document with changes...
except:
raise ndb.Rollback
index.put(old_doc)
finally:
# release lock
client.delete(doc_id)
I don't expect access to any particular document will be particularly contentious, and I think that Memcache lock keys will be short-lived enough that the risk of them getting booted is relatively low. If it becomes an issue, I could always put the locks in the datastore.
The reason I'm asking this question is because I don't have a ton of actual experience with web development or concurrency. Are there any race conditions or edge cases I missed? Other than the obvious improvements (e.g. backoff on lock acquiring, putting it all in a context manager), is there any reason this wouldn't be a good idea?