RavenDB concurrency, locking documents or the likes?

672 views Asked by At

I'm not 100% sure how to address this problem I have, so I'll try to illustrate my problem first. Please ignore that it's a bad example or a bad design of data structure, because this is simply an example.

Imagine we have a class called Person. The Person class has a standard properties that you would expect (Name, Age, etc). It also has a simple PersonId as well and a CompanyId. Further to do this it has a special property called Code.

Here in the business case, Code needs to be unique against each CompanyId. So it essentially is a composite key structure of CompanyId + Code.

I need to make it so that when there are concurrent applications creating these Person entities, they need to be able to abide by this constraint.

I've looked at various ways to approach this:

1) I know there is a Raven UniqueConstraints plugin to support some of these scenarios, but plugins is something I want to avoid in this situation

2) At first I thought making my Person document's raven ID to be the composite key of CompanyId + Code. However there's business requires to allow the user to change the Code. So that means if a user edits the code, we will end up creating a new Person record with the CompanyId + NewCodeSpecified as a new composite key ID. Then the previous record will still be floating around. Sure I can delete it, but it feels a bit dirty?

3) I stick with having unique Person Id's, and when any applications hit the endpoint to create a new Person, I perform a read from raven to see if there's a person record that contain the same Company ID and Code that I want to create. So it'll be something like:

  • Read person record
  • Person record does not exist
  • Proceed to creating new Person record.

Sure that's ok in simplistic scenario's

But what if we've got the rare case of two applications running concurrently and hitting the API service (Which is load balanced) simultaneously.

So then it'll be like:

App1                           App2
-----                          ----
Read person record             Read person record
Verify it does not exist yet   Verify it does not exist yet
Proceed to save new person     Proceed to save new person

So they will both end up making a Person record of the same Company Id + Code.

I know of the UseOptimisticConcurrency option, but I believe that will only throw a ConcurrencyException only if they are both attempting to modify the same record, and the ETAG checking kicks on SaveChanges.

So I'm a little stumped as to how I deal with this scenario.

1

There are 1 answers

0
gaunacode.com On

Going out on a limb here but...

You could make a new document called CodeConstraint. I've seen NServiceBus Saga persistence do something similar. It would look something like this:

public class CodeConstraint
{
   public string Id { get; set;} //you manually provide the id
   public string PersonId { get; set;}
}

You would then create a Person then a CodeContraint and assign the code constraint to the Person. You could wrap this into a TransactionScope such that you don't have orphan documents in case it fails to write.

Also, because this is it's own document, Raven will give you concurrency exceptions if you have any race conditions.

If the user wants to change the code. Then you create a new 'CodeConstraint' and assign it to the Person and keep or delete the old one.

For bonuses, you could extend the CodeContraint document with timestamps for when it was modified or created since you mentioned part of your requirements is to allow the users to customize this code.