def test_read_conflict_rollback_not_reintroduced(): db = StubDb() rollback = StubRollback() qa = qube.init({'test': set()}) qb = qube.init({'test': set()}) qube.apply_op(qa, ('add', 'test', 'abc', 'tx0')) qube.apply_op(qa, ('rem', 'test', 'abc', 'tx456')) qube.apply_op(qb, ('add', 'test', 'abc', 'tx0')) qube.apply_op(qb, ('rem', 'test', 'abc', 'tx456')) qube.rollback(qa, 'tx456') qube.apply_op(qa, ('add', 'test', 'def', 'tx789')) qube.apply_op(qb, ('add', 'test', 'ghi', 'tx321')) def doraise(a, b): raise riak.Conflict('the_vclock', 'x/zzz', [StubDbResult(qube.to_json(qa)), StubDbResult(qube.to_json(qb))]) db.get.side_effect = doraise m = yield from FooModel.read(db, rollback, 'zzz') assert_that(m.qube['journal'], has_length(3)) assert_that(m.qube['data'], has_entry('test', {'abc', 'def', 'ghi'})) assert_that(db.save, called_once_with('foo_bucket', 'zzz', has_key('data')))
def read(cls, db, rollback, key): logger.debug("reading %s/%s", cls.bucket, key) modified = False rollbacks = rollback.txs @contextlib.contextmanager def queue_rollback(op): try: yield except Exception as e: logger.debug('operation failed %r', op) cls.queue_rollback(rollback, op) try: response = yield from db.get(cls.bucket, key) except riak.Conflict as e: logger.debug("conflict in %s/%s", cls.bucket, key) modified = True first = e.siblings.pop() links = first.links vclock = e.vclock data = qube.from_json(first) for tx in rollbacks: qube.rollback(data, tx, error=queue_rollback) for r in e.siblings: r = qube.from_json(r) for tx in rollbacks: qube.rollback(r, tx, error=queue_rollback) data = qube.merge(data, r, error=queue_rollback) except: logger.error("Other error reading", exc_info=True) raise else: links = response.links vclock = response.vclock data = qube.from_json(response) if rollbacks: logger.debug("processing rollbacks in %s/%s %r", cls.bucket, key, rollbacks) try: seq = data['sequence'] for tx in rollbacks: data = qube.rollback(data, tx, error=queue_rollback) modified = seq != data['sequence'] except: logger.error("error performing rollback", exc_info=True) raise model = cls(key, data, vclock=vclock, links=links) if modified: # Ideally this would happen after returning the list # TODO: Task() yield from model.save(db) yield from rollback.process(db) logger.debug('read %s/%s %s', cls.bucket, key, vclock) return model