Skip to content

Instantly share code, notes, and snippets.

@RedBeard0531
Last active August 29, 2015 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RedBeard0531/d3526dfdd5edfbca25c3 to your computer and use it in GitHub Desktop.
Save RedBeard0531/d3526dfdd5edfbca25c3 to your computer and use it in GitHub Desktop.
Write Unit of Work Example Code
// pseudo-c++
//
// High Level Examples
//
void insert(BSONObj obj, bool shouldLogOp) {
do {
WriteUnitOfWork wunit;
if (!collection)
collection = createCollection();
collection->insertDoc(obj);
if (shouldLogOp)
logOp_insert(obj);
wunit.commit();
} while (should retry);
}
void update(UpdateRequest request) {
for (doc in docsMatching(request.query)) {
do {
WriteUnitOfWork wunit;
Lock lk(doc);
collection->updateDoc(obj);
if (request.logOp)
logOp_update(...);
wunit.commit();
} while (should retry);
}
if (request.upsert && nothing matched) {
insert(obj, request.logOp);
}
}
void applyOps(vector<Op> ops) {
BigGlobalXLock lk; // We already do this. Leaving it has the advantage of preventing deadlock.
WriteUnitOfWork wunit; // All inner changes only become visible once this commits.
for (op in ops) {
switch(op.type()) {
// This won't work for indexes or other "large" ops. They must be handled outside of any WUOW
UPDATE: update(op); break;
DELETE: delete(op); break;
INSERT: insert(op); break;
COMMAND: runCommand(op); break;
}
}
logOp_command(ops)
wunit.commit();
}
void Collection::insertDoc(BSONObj obj) {
RecordId id = recordStore->insertDoc(obj);
// can rely on the insert not being public until wunit completed
index(obj);
}
void Collection::updateDoc(...) {
Lock lk(id) // maybe can rely on caller doing the locking
RecordId newId = recordStore->updateDoc(...);
// can rely on the newId not being public until wunit completed
adjust_indexes(obj, id, newId);
}
//
// Possible impls
//
class WriteUnitOfWork {
WriteUnitOfWork() {
txn->lockState->beginUOW();
txn->recoveryUnit->beginUOW();
}
commit() {
txn->recoveryUnit->commitUOW();
// I don't think LockState needs to distinguish between commit vs rollback.
// Also, this may be unnessesary as we will probably be going to the destructor soon.
// This is a question of whether we allow multiple commits on a single wunit or if the
// commit marks the "end" and a new one must be used.
txn->lockState->endUOW();
txn->lockState->beginUOW();
}
~WriteUnitOfWork() {
txn->recoveryUnit->endUOW();
txn->lockState->endUOW();
}
}
class LockState/Transaction {
void beginUOW() { nestingLevel++; }
void endUOW() {
if (--nestingLevel == 0) {
// When exiting outmost WUOW, unlock all delayed locks.
for (recordId in releaseQueue) {
releaseReal(recordId);
}
releaseQueue.clear();
}
}
void aquire(ResourceId rid) { aquireReal(rid); }
void release(ResourceId rid) {
if (nestingLevel != 0) {
// We are in a WUOW so we queue the unlock until we exit.
releaseQueue.push(rid);
return;
}
releaseReal(rid);
}
int nestingLevel = 0;
queue<RecordId> releaseQueue;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment