Skip to main content

Design a unique sequence with MongoDB

Greetings!

If you have used RDBMS like MySQL or Oracle you know that those databases support auto-increment fields or sequences. However MongoDB's way of ID is using an ObjectId which is a large string. However if you want an unique sequence what will you do?

One solution is of cause to use RDBMS solution like Oracle. Unlike NoSQL solutions, RDBMS have problems with scaling. Let's imagine that to overcome scaling problems we are using MongoDB.

Why do we need a unique sequence?

We will need unique sequences for several reasons.
  • Design a hit counter.
  • Short string generation (https://www.slmanju.com/2021/07/basebase62-encoding-with-java.html).
  • Rate limiting.
  • Easy to read identifier value.
  • You are migrating from relational database.

Eventhough MongoDB doesn't offer this feature out of box, we can implement it using it's atomic operations.

The trick is to create a collection with desired name as the _id and findAndModify with $inc.

db.counters.insert({
  _id: "my-sequence",
  sequence: 0
}
db.counters.findAndModify({
  query: { _id: "my-sequence" },
  update: { $inc: { sequence: 1 } },
  new: true
})


Downside

As this is atomic operation we can serve only 1 thread at a time. Hence we have to carefull with the performance panelty. Also, this would be a single point of failure.
However, I could run 15,000 counts per second on average in my Intel 1.80GHz 4 core 8 threads CPU, 8GB RAM, Ubuntu 18 Dell Laptop.

Implement with Spring MongoDB

Now we can use this trick in our applications easily. As the Spring framework provides the MongoDB integration, all we need to do is call the appropriate methods.

First, we can create our Collection like this.

@Document(collection = "counters")
@Getter @Setter
public class Counter {
  @Id
  private String id;
  private long sequence;
}
Then use Spring MongoDB methods to findAndModifiy the sequence.

@Component
public class SequenceGenerator {

  @Autowired
  private MongoTemplate mongoTemplate;

  public long findNext() {
    Query query = query(where("_id").is("url_shortner"));
    Update update = new Update().inc("sequence", 1);
    Counter counter = mongoTemplate.findAndModify(query, update, options().returnNew(true), Counter.class);
    return counter.getSequence();
  }
}
long sequence = sequenceGenerator.findNext();
It is extremely fun learning experience anyway :)

Happy coding.