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.
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:You would then create a
Person
then aCodeContraint
and assign the code constraint to thePerson
. You could wrap this into aTransactionScope
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.