core (memory, sqlite): added fileUpdate method to update "extra" and "access" avoiding race condition
This commit is contained in:
parent
50a7be4fe4
commit
97f19b2e52
2 changed files with 54 additions and 0 deletions
|
|
@ -798,6 +798,48 @@ class SqliteStorage(object):
|
|||
d.addErrback(lambda failure: log.error(_(u"Can't save file metadata for [{profile}]: {reason}".format(profile=client.profile, reason=failure))))
|
||||
return d
|
||||
|
||||
def _fileUpdate(self, cursor, file_id, column, update_cb):
|
||||
query = 'SELECT {column} FROM files where id=?'.format(column=column)
|
||||
for i in xrange(5):
|
||||
cursor.execute(query, [file_id])
|
||||
try:
|
||||
older_value_raw = cursor.fetchone()[0]
|
||||
except TypeError:
|
||||
raise exceptions.NotFound
|
||||
value = json.loads(older_value_raw)
|
||||
update_cb(value)
|
||||
value_raw = json.dumps(value)
|
||||
update_query = 'UPDATE files SET {column}=? WHERE id=? AND {column}=?'.format(column=column)
|
||||
update_args = (value_raw, file_id, older_value_raw)
|
||||
try:
|
||||
cursor.execute(update_query, update_args)
|
||||
except sqlite3.Error:
|
||||
pass
|
||||
else:
|
||||
if cursor.rowcount == 1:
|
||||
break;
|
||||
log.warning(_(u"table not updated, probably due to race condition, trying again ({tries})").format(tries=i+1))
|
||||
else:
|
||||
log.error(_(u"Can't update file table"))
|
||||
|
||||
def fileUpdate(self, file_id, column, update_cb):
|
||||
"""update a column value using a method to avoid race conditions
|
||||
|
||||
the older value will be retrieved from database, then update_cb will be applied
|
||||
to update it, and file will be updated checking that older value has not been changed meanwhile
|
||||
by an other user. If it has changed, it tries again a couple of times before failing
|
||||
@param column(str): column name (only "access" or "extra" are allowed)
|
||||
@param update_cb(callable): method to update the value of the colum
|
||||
the method will take older value as argument, and must update it in place
|
||||
update_cb must not care about serialization,
|
||||
it get the deserialized data (i.e. a Python object) directly
|
||||
Note that the callable must be thread-safe
|
||||
@raise exceptions.NotFound: there is not file with this id
|
||||
"""
|
||||
if column not in ('access', 'extra'):
|
||||
raise exceptions.InternalError('bad column name')
|
||||
return self.dbpool.runInteraction(self._fileUpdate, file_id, column, update_cb)
|
||||
|
||||
##Helper methods##
|
||||
|
||||
def __getFirstResult(self, result):
|
||||
|
|
|
|||
Loading…
Reference in a new issue