def test_threeway_merge(): qa = qube.init({'name': 'zoidberg', 'number': 5, 'value': 10}) qube.apply_op(qa, ('change', 'number', (5, 6), 'tx001')) qb = qube.from_json(qube.to_json(qa)) qc = qube.from_json(qube.to_json(qa)) qube.apply_op(qa, ('change', 'name', ('zoidberg', 'bob'), 'tx002')) qube.apply_op(qb, ('change', 'number', (6, 7), 'tx003')) qube.apply_op(qc, ('change', 'value', (10, 9), 'tx004')) error, watch = track_error() m = qube.merge(qa, qb, qc, error=watch) assert_that(m, has_entry('sequence', 4)) assert_that(m, has_entry('data', has_entry('name', 'bob'))) assert_that(m, has_entry('data', has_entry('number', 7))) assert_that(m, has_entry('data', has_entry('value', 9))) assert_that(error, has_length(0))
def test_step_merge_bside(): qa = qube.init({'name': 'zoidberg', 'number': 5, 'value': 10}) qube.apply_op(qa, ('change', 'number', (5, 6), 'tx001')) qb = qube.from_json(qube.to_json(qa)) qube.apply_op(qa, ('change', 'name', ('zoidberg', 'bob'), 'tx002')) qube.apply_op(qb, ('change', 'number', (6, 7), 'tx004')) qc = qube.from_json(qube.to_json(qb)) qube.apply_op(qc, ('change', 'value', (10, 9), 'tx004')) pprint(qa) pprint(qb) pprint(qc) error, watch = track_error() ma = qube.merge(qa, qb, error=watch) assert_that(error, has_length(0)) assert_that(ma, has_entry('sequence', 3)) assert_that(ma, has_entry('data', has_entry('name', 'bob'))) assert_that(ma, has_entry('data', has_entry('number', 7))) assert_that(ma, has_entry('data', has_entry('value', 10))) print('---') pprint(ma) pprint(qc) error, watch = track_error() mb = qube.merge(ma, qc, error=watch) assert_that(error, has_length(0)) assert_that(mb, has_entry('sequence', 4)) assert_that(mb, has_entry('data', has_entry('name', 'bob'))) assert_that(mb, has_entry('data', has_entry('number', 7))) assert_that(mb, has_entry('data', has_entry('value', 9)))
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