Ming
Ming

Reputation: 1693

How do you do atomic, multi-record, inter-dependent operations in NoSQL?

I'm originally familiar with relational-style data stores. I'm currently looking into NoSQL and trying to learn about its use-cases. Here's something that's been bugging me lately.

How do you do the following operation using the typical NoSQL product?

An example of this, from past experience with other issues, is as follows. You have a web game with various user accounts. Users can initiate attacks on each other, where the attack formula is complicated black-box game-logic which determines the mutual outcome based on both inputs and randomness. You need to enforce that an attack happens atomically, and that the inputs reflect a consistent point in time, as do the outputs, with regard to some serialization of the sequence of attacks performed in the game.

It's important that:

My particular example was an issue we encountered. We were actually using a relational database, but we were not using its transactional features like we should have. As a result, online players attacking each other repeatedly tended to generate incorrect results and generate phantom resources for both attackers.

In the relational model, I would have done this using a transaction. Come to think of it, this may be a textbook example of transactions.

How would I go about achieving this in NoSQL?

Here are answers I have seen on SO and elsewhere which I consider a bad fit for this particular instance:

... although if you believe any or all of them is a good solution, please let me know how and why.

I would appreciate some pointers on how to implement this sort of thing in practice.

Thanks!

Upvotes: 3

Views: 855

Answers (1)

Tyler Brock
Tyler Brock

Reputation: 30176

The two things you need to know about MongoDB regarding this topic are:

  • The unit of atomicity is a single document.
  • There are no transactions but you can simulate them.

Keeping that in mind, this sort of thing isn't MongoDB's strength as, and you point this out yourself, it is essentially a transaction.

However, if you were to try and model this you could start by creating a collection for attacks where the documents look like this:

{
  attacker: {user_document},
  attackee: {user_document},
  in_progress:  boolean (true or false),
  outcomes: [array of results based on calculation]
}

To start an attack you would query the attacks collection using a findAndModify with an upsert having a query document that contains both user ids. If one does exist, it will not create a new attack. If one doesn't exist, it will begin the attack by inserting the document and setting in_progress to true. Put all the necessary user details in there.

Then, do the black box calculation and push a series of updates that would need to occur to both user documents as a result of the attack onto the outcomes array and set in_progress to false.

When finished, apply the outcomes to the users collection one by one.

If no more outcomes exist, remove the attack document so that a new attack can begin.

Not sure if this would make complete sense given your requirements but hopefully this helps you think about how it could be done.

Upvotes: 2

Related Questions