Atomic counters are a necessity for a large number of use cases. Mongo doesn't have a built-in feature for atomic counters; nevertheless, it can be easily implemented using some of its cool offerings. In fact, implementing it is merely a couple of lines of code. Refer to the previous recipe to know what atomic find
and modify
operations are in Mongo.
Refer to the Single node installation of MongoDB recipe in Chapter 1, Installing and Starting the MongoDB Server, and start a single instance of Mongo. This is the only prerequisite for this recipe. Start a Mongo shell and connect to the started server.
> function getNextSequence(counterId) { return db.counters.findAndModify( { query: {_id : counterId}, update: {$inc : {count : 1}}, upsert: true, fields:{count:1, _id:0}, new: true } ).count }
> getNextSequence('Posts Counter') > getNextSequence('Posts Counter') > getNextSequence('Profile Counter')
The function is as simple as a findAndModify
operation on a collection used to store all the counters. The counter ID is the _id
field of the document stored, and the value of the counter is stored in the count
field. The document passed to findAndModify
accepts the query that uniquely identifies the document storing the current count, which is a query using the _id
field. The update
operation is an $inc
operation that will increment the value of the count
field by one, but what if the document doesn't exist? This will happen during the first invocation of the counter. To take care of this scenario, we will be setting the upsert
flag to true
, which atomically either updates the document field or creates one. The value, thus, will always start with one, and there are no ways in this function by which we can have any user-defined start number for the sequence and a custom-incremented step. To address such requirements, we will have to specifically add a document with the initialized values to the counters
collection. Finally, we are interested in the state of the counter after the value is incremented. Hence, we set the value of the new
field as true
.
On invoking this method three times, as we did, we should see the following in the counters
collection. Simply execute the following query:
> db.counters.find() { "_id" : "Posts Counter", "count" : 2 } { "_id" : "Profile Counter", "count" : 1 }
Using this small function, we have now implemented atomic counters in Mongo.
We can store such common code on the Mongo server, which will be available for execution in other functions.