race conditions

63 views
Skip to first unread message

Phillip Haydon

unread,
Aug 28, 2015, 8:09:21 AM8/28/15
to RavenDB - 2nd generation document database
I have a scenario where I have an event raised in the system and interaction from the user.

In some* scenarios (happened 3 times in 2 months) if the event is processed at the same time the user does something on the same document, both with modifying different data. It ends up that they override each other. 

Trying to figure out if I'm doing something wrong. Or something I can do to prevent that happening.

example:

User uploads a photo

While adding information to the photo, like Name / Description.

I process the photo and pull out metadata like camera info to put on the photo. 
The camera processing one opens the document at:

04:45:10

It takes 3 seconds to grab the info and attach it to the document.

Commits at 04:45:13

The user changes the name of the photo.

Document is opened at 04:45:11, name changed, and committed straight away.

When the image is processed and committed at 04:45:13, it loses the name change.

:(



Kijana Woodard

unread,
Aug 28, 2015, 8:18:31 AM8/28/15
to rav...@googlegroups.com
And you have optimistic concurrency turned on?

From: Phillip Haydon
Sent: ‎8/‎28/‎2015 7:09 AM
To: RavenDB - 2nd generation document database
Subject: [RavenDB] race conditions

--
You received this message because you are subscribed to the Google Groups "RavenDB - 2nd generation document database" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ravendb+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Phillip Haydon

unread,
Aug 28, 2015, 8:23:59 AM8/28/15
to RavenDB - 2nd generation document database
No, I thought about it but I decided it wasn't needed because int he scenario I would never change the same fields.

Am I going to need to enable it? Is there a method of retrying?

Phillip Haydon

unread,
Aug 28, 2015, 8:25:39 AM8/28/15
to RavenDB - 2nd generation document database
Also, I'm (at the moment) changing the image processing to query the document twice, first time to get info i need to go process it, then just before I need to save the changes. 

Kijana Woodard

unread,
Aug 28, 2015, 8:31:01 AM8/28/15
to rav...@googlegroups.com
The second query will only make a difference if it's new session. Optimistic concurrency is at the document level, not the property level.

Fwiw, I turn on optimistic concurrency by default at document store setup. I turn it off in the rare situations where I want last write wins.

From: Phillip Haydon
Sent: ‎8/‎28/‎2015 7:25 AM
Subject: Re: [RavenDB] race conditions

Phillip Haydon

unread,
Aug 28, 2015, 8:43:49 AM8/28/15
to RavenDB - 2nd generation document database
Enabled for both web and service at store setup. Will see if I get any exceptions from this.

Thanks.

Kijana Woodard

unread,
Aug 28, 2015, 8:48:01 AM8/28/15
to rav...@googlegroups.com
For web, it's rare to see conflicts from users, assuming document design with this in mind.

For multi threaded services processing automated requests, it happens fairly often. Generally I'm using NServiceBus which grants retries for such issues.

From: Phillip Haydon
Sent: ‎8/‎28/‎2015 7:43 AM

Chris Marisic

unread,
Aug 28, 2015, 10:52:40 AM8/28/15
to RavenDB - 2nd generation document database
If name is the only field you care about in this sense. I would just add a property "NameLastUpdatedAt"

I would only overwrite a name property when the value is greater than the persisted one.

Also i pretty much never do session.Store(object-from-the-wire) i will always have obj = Load(id), obj.Name = postModel.Name, session.Store(obj) session.SaveChanges() 

and yes i know i don't need .Store in that later usage but it i like it for its explicitness, i don't like phantom database mutation merely calling SaveChanges() [die orms, die /rant]

Kijana Woodard

unread,
Aug 28, 2015, 11:30:37 AM8/28/15
to rav...@googlegroups.com
+1 on the mutation. 

I'd like a way to store which is "I'll take this object and see if I have something in session with the same Id; if so, I'll replace the session object with it".

My assumption is this feature alone makes working with ravendb in F# fairly awkward.

It can catch you off guard in C# as well, especially if one has the infrastructure calling SaveChanges. [ProTip: don't call SaveChanges implicitly for GET requests and the response code should be 2xx]

Oren Eini (Ayende Rahien)

unread,
Aug 28, 2015, 12:02:33 PM8/28/15
to ravendb
When you save a document in RavenDB (unless you are using patching), we save the _full_ document.

Hibernating Rhinos Ltd  

Oren Eini l CEO Mobile: + 972-52-548-6969

Office: +972-4-622-7811 l Fax: +972-153-4-622-7811

 

Oren Eini (Ayende Rahien)

unread,
Aug 28, 2015, 12:03:21 PM8/28/15
to ravendb
That doesn't solve a race condition.
Only actually using optimistic concurrency would


Hibernating Rhinos Ltd  

Oren Eini l CEO Mobile: + 972-52-548-6969

Office: +972-4-622-7811 l Fax: +972-153-4-622-7811

 


Chris Marisic

unread,
Aug 28, 2015, 1:05:27 PM8/28/15
to RavenDB - 2nd generation document database
Race condition isn't the fitting term here. What Philip is talking about is actually a timing/sequencing/order of operations issue. Optimistic Concurrency doesn't solve Name -> Name-2 -> Name, when the desired final value is Name-2.

The  NameLastUpdatedAt is enforcing ordering of updates.

But yes i agree, true race conditions require Optimistic Concurrency. I would never use RavenDB without Optimistic Concurrency. 

Oren Eini (Ayende Rahien)

unread,
Aug 28, 2015, 1:19:00 PM8/28/15
to ravendb
NameLastUpdatedAt won't.
And it is a race condition. You read it at time A (NameLastUpdatedAt  is valid as far as you can see), someone changes that, you write it back.
If you don't use optimistic concurrency, you overwrote data.

Chris Marisic

unread,
Aug 28, 2015, 1:27:33 PM8/28/15
to RavenDB - 2nd generation document database
Without doing NameLastUpdateAt, getting a concurrency exception if your policy is just retry. The retry will be fine and name will be Name, not Name-2

Oren Eini (Ayende Rahien)

unread,
Aug 28, 2015, 1:29:12 PM8/28/15
to ravendb
No, you reload the document, the name in Name2.

Chris Marisic

unread,
Aug 28, 2015, 1:36:36 PM8/28/15
to RavenDB - 2nd generation document database
Separately. What you're doing is really just a violation of SRP. Whatever takes the 3 seconds to process, should have absolutely nothing to do with the user visible and configurable name.

Even if Name is included in the metadata, who cares if the user desyncs it? If you want the metadata name that's entirely fine to be different than the display-name 


On Friday, August 28, 2015 at 7:09:21 AM UTC-5, Phillip Haydon wrote:

Phillip Haydon

unread,
Aug 31, 2015, 10:43:45 AM8/31/15
to RavenDB - 2nd generation document database
The thing with this document is the processing is done shortly after creation of the document. After that the user may edit it for up to 10 minutes depending on how long it takes him to edit it. This is always done by 1 person and never more than 1. 

Infact every operation I have is always done at 1 time and everything else is evented / stored, and then Map/Reduce. Never updating the original document unless the user decides to edit his own document in the future, again only 1 actor involved.

It just happens the processing needed to update the same document. If the event fails it would retry and chances are would never fail the second time round.

I have changed it however to re-load the document, and modify it and save it to reduce that 3s window down to ~1s after processing is done. So its Load -> Get Data -> Close -> Process -> Load -> Update -> Save Changes.

Chris Marisic

unread,
Aug 31, 2015, 10:57:52 AM8/31/15
to RavenDB - 2nd generation document database


On Monday, August 31, 2015 at 9:43:45 AM UTC-5, Phillip Haydon wrote:

It just happens the processing needed to update the same document. If the event fails it would retry and chances are would never fail the second time round.

Why does it have to be the same document? 

You can easily partition the document and Foo/1 and Foo/1/Details and use LoadStartingWith or Includes to bring it back contiguous for presentation.
Reply all
Reply to author
Forward
0 new messages