Improve handling of mis-writes

archive/original-v2.1
Mike Lang 6 years ago
parent dcb6d6ba6d
commit 1aa8f905f4

@ -2,12 +2,16 @@
which transparently handles re-connecting, sheets schemas and tracking rows by id. which transparently handles re-connecting, sheets schemas and tracking rows by id.
""" """
import random
import string
import gevent.lock import gevent.lock
from oauth2client.client import SignedJwtAssertionCredentials from oauth2client.client import SignedJwtAssertionCredentials
import gspread import gspread
from . import states
# schemas maps sheet name to schema. # schemas maps sheet name to schema.
# each schema contains a map from column names to column indexes (1-based) # each schema contains a map from column names to column indexes (1-based)
@ -148,8 +152,7 @@ class Sheet(object):
class Row(object): class Row(object):
"""Represents a row in a sheet. Values can be looked up by attribute. """Represents a row in a sheet. Values can be looked up by attribute.
Values can be updated with update(attr=value), which returns the updated row. Values can be updated with update(attr=value), which returns the updated row.
Note that a row must have an id to be updatable. Updating is permitted if no id is set If a row without an id is updated, an id will be randomly assigned.
only if id is one of the values being written.
You can also refresh the row (if it has an id) by calling row.refresh(), You can also refresh the row (if it has an id) by calling row.refresh(),
which returns the newly read row, or None if it can no longer be found. which returns the newly read row, or None if it can no longer be found.
""" """
@ -171,24 +174,38 @@ class Row(object):
return self.values[col] return self.values[col]
return "" return ""
def _raw_update(self, name, value):
col = self.schema[name]
self.sheet.worksheet.update_cell(self.index, col, value)
def update(self, **values): def update(self, **values):
with self.manager.lock: with self.manager.lock:
self.manager.refresh() self.manager.refresh()
while True:
# We attempt to detect races by: # We attempt to detect races by:
# Always refreshing our position before we begin (if we can) # Always refreshing our position before we begin (if we can)
# Checking our position again afterwards. If it's changed, we probably mis-wrote. # Checking our position again afterwards. If it's changed, we probably mis-wrote.
if self.id: if self.id:
before = self.refresh() before = self.refresh()
# TODO handle before = None if before is None:
raise Exception("Cannot update row {}: Row is gone".format(self))
else: else:
before = self before = self
if 'id' not in values:
# auto-create id
values['id'] = ''.join(random.choice(string.letters + string.digits) for _ in range(12))
for name, value in values.items(): for name, value in values.items():
col = self.schema[name] before._raw_update(name, value)
self.sheet.worksheet.update_cell(before.index, col, value)
after = self.sheet.by_index(before.index) after = self.sheet.by_index(before.index)
new_id = values['id'] if 'id' in values else self.id new_id = values['id'] if 'id' in values else self.id
if after.id != new_id: if after.id != new_id:
raise Exception("Likely bad write: Row {} may have had row {} data partially written: {}".format(after, before, values)) logging.error("Likely bad write: Row {} may have had row {} data partially written: {}".format(after, before, values))
if hasattr(after, 'state'):
after._raw_update('state', states.ERROR)
if hasattr(after, 'notes'):
after._raw_update('notes', "This row may have had following data from row with id {} written to it: {}".format(new_id, values))
continue # retry
return
def refresh(self): def refresh(self):
return self.sheet[self.id] return self.sheet[self.id]

Loading…
Cancel
Save