Last active
August 29, 2015 14:03
-
-
Save RedBeard0531/d3526dfdd5edfbca25c3 to your computer and use it in GitHub Desktop.
Write Unit of Work Example Code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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