Mongo - Custom pessimistic locking mechanism

April 13, 2017

Problem:

Having one thread updating a document at a time.

Solution:

Lock manager:
<![CDATA[ /** * MongoDB document locking manager * (custom solution) */ @Component public class MongoLockManager { private WriteConcern WRITE_CONCERN = WriteConcern.MAJORITY; private int TIMEOUT = 2; private TimeUnit TIMEOUT_TIME_UNIT = TimeUnit.SECONDS; @Autowired private MongoClient mongoClient; @Value(“${spring.application.name}") private String applicationName; private static final Logger logger = LoggerFactory.getLogger(MongoLockManager.class); /** * Inserts a document id in the product full details locking management collection * @param document the document containing the id to getLock */ public void insert(Document document) { Lock lock = new Lock(document.get_id().toHexString(), Calendar.getInstance().getTimeInMillis(), applicationName); mongoClient.insert(lock); logger.info("{}", ); } /** * Searches for a document id in the product full details locking management collection * @param document the document containing the id to getLock * @return */ public boolean getLock(Document document) { long ts = Calendar.getInstance().getTimeInMillis(); Query query = new Query(Criteria.where("_id”).is(document.get_id().toHexString())); Update update = new Update().setOnInsert("_id”, document.get_id().toHexString()) .setOnInsert(“ts”, ts); FindAndModifyOptions findAndModifyOptions = new FindAndModifyOptions(); findAndModifyOptions.returnNew(true); findAndModifyOptions.upsert(true); WriteConcern writeConcern = WRITE_CONCERN .withWTimeout(TIMEOUT, TIMEOUT_TIME_UNIT); Lock lock = (Lock) mongoClient.findAndModify(query, update, writeConcern, findAndModifyOptions, new Lock()); if(lock.getTs() == ts) { logger.info("{}", ); return true; } else { logger.info("{}", ); return false; } } /** * Removes a document id from the product full details locking management collection * @param productFullDetails the document containing the id to getLock */ public void unlock(ProductFullDetails productFullDetails) { Query query = new Query(Criteria.where("_id”).is(document.get_id().toHexString())); mongoClient.remove(query, new Lock()); logger.info("{}", ); } } ]]>
SyntaxHighlighter.highlight();
, lock implementation:
<![CDATA[ /** * Models a mongoDB getLock * (custom solution) */ @Document(collection = “Locks”) public class Lock { // document id on which the getLock is acquired private String _id; // timestamp private long ts; // application id private String aid; public Lock() { } /** * * @param _id document id on which the getLock is acquired * @param ts timestamp * @param aid application id */ public Lock(String _id, long ts, String aid) { this._id = _id; this.ts = ts; this.aid = aid; } public long getTs() { return this.ts; } } ]]>
SyntaxHighlighter.highlight();
, how to:

  1. check if the document is locked
  2. acquire the lock
  3. release the lock

<![CDATA[ while(!mongoLockManager.getLock(document)) { try { Thread.sleep(1000); } catch (InterruptedException e) { logger.warn("{}", ); } } mongoLockManager.unlock(document); ]]>
SyntaxHighlighter.highlight();