The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

nlif (4) [Avatar] Offline
#1
Hi Chris,

Great book!

I've got a question: in ch.12 you describe how, in case of a concurrency failure, we should rollback and retry the transaction. I don't understand why we should retry. Assuming we're talking about an optimistic locking scenario, then a concurrnecy failure means that someone else has updated our record after we loaded it, so if we go ahead with our update, we will cause a "lost update" problem. First, I fail to understand how subsequent attempts will succeed - our version (or timestamp) is now older than the value in the database, and this will not change regardless of how many times we retry. Second, why would we even want to retry - doesn't it make more sense to abort? After all - someone else has changed the data before we did, so it seems more sensible to return to the user with the new data, and let him decide what to do.

Thanks.
dgalehouse (7) [Avatar] Offline
#2
Re: Optimistic Locking
> Hi Chris,
>
> Great book!

Agreed! The book is a pleasure to read! The material ties together many of the concepts from some of the books I have read recently (poEAA, DDD, Pro Spring etc.) in a way that I didn't think possible. Suddenly, the complexity inherent in learning so many new APIs and design approaches seems manageable! Somehow, this book manages to go deep and wide and still be fun to read!


>
> I've got a question: in ch.12 you describe how, in
> case of a concurrency failure, we should rollback and
> retry the transaction. I don't understand why we
> should retry. Assuming we're talking about an
> optimistic locking scenario, then a concurrnecy
> failure means that someone else has updated our
> record after we loaded it, so if we go ahead with our
> update, we will cause a "lost update" problem. First,
> I fail to understand how subsequent attempts will
> succeed - our version (or timestamp) is now older
> than the value in the database, and this will not
> change regardless of how many times we retry. Second,
> why would we even want to retry - doesn't it make
> more sense to abort? After all - someone else has
> changed the data before we did, so it seems more
> sensible to return to the user with the new data, and
> let him decide what to do.
>
> Thanks.


I believe it's implied that the stale object is thrown out and refreshed from persistent storage and then another attempt is made to apply changes and update.
nlif (4) [Avatar] Offline
#3
Re: Optimistic Locking
Thanks for the reply.

Let me see if I understand what you're suggesting: upon catching the exception, the object is loaded from the database, and then the rejected changes are *merged* with the loaded object, and then we retry - and this time we succeed. Well, this certainly makes more sense!

By the way, the sample RetryInterceptor which is included in the book's source code has a cleanupBeforeRetrying() method, which may *imply* such approach (it is empty, but protected, so subclasses could add such code in there).

As for the merging strategy, I guess if the changes do not conflict we simply merge without user approval, but if they do - we have to get back to the user, display the relevant data, and ask him to decide.

This means we need to compare our version with the database version, and perform a diff() operation. So we need to add a diff() operation for domain objects.
dgalehouse (7) [Avatar] Offline
#4
Re: Optimistic Locking
> Thanks for the reply.
>
> Let me see if I understand what you're suggesting:
> upon catching the exception, the object is loaded
> from the database, and then the rejected changes are
> *merged* with the loaded object, and then we retry -
> and this time we succeed. Well, this certainly makes
> more sense!

That's my understanding.

>
> By the way, the sample RetryInterceptor which is
> included in the book's source code has a
> cleanupBeforeRetrying() method, which may *imply*
> such approach (it is empty, but protected, so
> subclasses could add such code in there).

Cool! I wasn't aware of RetryInterceptor. Thanks for the nudge!

>
> As for the merging strategy, I guess if the changes
> do not conflict we simply merge without user
> approval, but if they do - we have to get back to the
> user, display the relevant data, and ask him to
> decide.

I can't think of a case with an optimistic locking approach where you would ask a user who previously updated an object to approve a subsequent update. I don't believe the semantics are the same as they are for a source code merge under a source control system. If you're the loser on an optimistic lock you are forced to refresh the persistent object and start all over. As far as the optimistic lock winner is concerned, his or her transaction was committed and they've moved on to other things. As mentioned in the book, among the criteria for deciding between an optimistic and pessimistic locking approach is the degree of frustration/inconvenience that would be felt by a user if forced to start over. If this is perceived to be a likely source of frustration then a pessimistic approach would be used.

>
> This means we need to compare our version with the
> database version, and perform a diff() operation. So
> we need to add a diff() operation for domain objects.

For the optimistic locking approaches in the book, you either win or lose and the row version number or timestamp would be enough to determine that. There are more elaborate schemes using state comparisons etc. that could be used if you were locked in to using a legacy database that didn't have row versions/time stamps.
nlif (4) [Avatar] Offline
#5
Re: Optimistic Locking
> I can't think of a case with an optimistic locking
> approach where you would ask a user who previously
> updated an object to approve a subsequent update. I
> don't believe the semantics are the same as they are
> for a source code merge under a source control
> system. If you're the loser on an optimistic lock you
> are forced to refresh the persistent object and start
> all over. As far as the optimistic lock winner is
> concerned, his or her transaction was committed and
> they've moved on to other things. As mentioned in the
> book, among the criteria for deciding between an
> optimistic and pessimistic locking approach is the
> degree of frustration/inconvenience that would be
> felt by a user if forced to start over. If this is
> perceived to be a likely source of frustration then a
> pessimistic approach would be used.


Let me try to explain what I mean:

Let's say we're both updating the same Customer's details. I change the address and commit first. You change the phone number, and commit second. Your update will get an exception, because of the version check. However, it is fairly sensible to assume, that both of us would be content with saving your updates on top of mine (i.e. a merge): my update (the new address) is not lost, so I'm happy, and your update (the new phone number) is not lost either, so you're happy too.

However, let's consider the same scenario, with one difference: both of us updated the customer's phone number. In this case, you (being the second to commit) should be made aware of the failure, and a merge is most likely a bad idea. Assuming we entered two different numbers, then the most sensible course of action is to make at least one of us (you, because you are the second to commit) aware of the conflict, and force us to figure out what is the correct new phone number for that customer.

Am I making more sense now?
dgalehouse (7) [Avatar] Offline
#6
Re: Optimistic Locking
> > I can't think of a case with an optimistic locking
> > approach where you would ask a user who previously
> > updated an object to approve a subsequent update.
> I
> > don't believe the semantics are the same as they
> are
> > for a source code merge under a source control
> > system. If you're the loser on an optimistic lock
> you
> > are forced to refresh the persistent object and
> start
> > all over. As far as the optimistic lock winner is
> > concerned, his or her transaction was committed
> and
> > they've moved on to other things. As mentioned in
> the
> > book, among the criteria for deciding between an
> > optimistic and pessimistic locking approach is the
> > degree of frustration/inconvenience that would be
> > felt by a user if forced to start over. If this is
> > perceived to be a likely source of frustration then
> a
> > pessimistic approach would be used.
>
>
> Let me try to explain what I mean:
>
> Let's say we're both updating the same Customer's
> details. I change the address and commit first. You
> change the phone number, and commit second. Your
> update will get an exception, because of the version
> check. However, it is fairly sensible to assume, that
> both of us would be content with saving your updates
> on top of mine (i.e. a merge): my update (the new
> address) is not lost, so I'm happy, and your update
> (the new phone number) is not lost either, so you're
> happy too.
>
> However, let's consider the same scenario, with one
> difference: both of us updated the customer's phone
> number. In this case, you (being the second to
> commit) should be made aware of the failure, and a
> merge is most likely a bad idea. Assuming we entered
> two different numbers, then the most sensible course
> of action is to make at least one of us (you, because
> you are the second to commit) aware of the conflict,
> and force us to figure out what is the correct new
> phone number for that customer.
>
> Am I making more sense now?

Of course! You've been making sense from the beginning!

It could be me not making sense!

I do understand what you're saying but I'm wondering if the scenario you describe might be better implemented with a pessimistic lock. Let's say that instead of getting blocked by an optimistic lock the second user retrieves the object from persistent storage a fraction of a second after the first user commits the update to the phone number and then updates it. Does that change anything? There may only be a couple of seconds difference between these two scenarios but in the first case the app would go to the trouble of comparing state and determining what has changed and in the second case the update would occur unimpeded. Two very different results but essentially the same situation. If the business rules really do dictate the type of sensitivity you describe, I'm thinking a pessimistic lock may be a better choice?
nlif (4) [Avatar] Offline
#7
Re: Optimistic Locking
> I do understand what you're saying but I'm wondering
> if the scenario you describe might be better
> implemented with a pessimistic lock. Let's say that
> instead of getting blocked by an optimistic lock the
> second user retrieves the object from persistent
> storage a fraction of a second after the first user
> commits the update to the phone number and then
> updates it. Does that change anything? There may only
> be a couple of seconds difference between these two
> scenarios but in the first case the app would go to
> the trouble of comparing state and determining what
> has changed and in the second case the update would
> occur unimpeded. Two very different results but
> essentially the same situation. If the business rules
> really do dictate the type of sensitivity you
> describe, I'm thinking a pessimistic lock may be a
> better choice?

Pessimistic Locking is certainly an alternative, but I am at the moment more interested in Optimistic Locking. My original question was about the handling of a version mismatch. The book seems to simply suggest that we "retry", and I was trying to understand what that means, exactly. I guess what we should do when we catch the concurrency failure exception, is the following: First we load the most recent version of the object from the database. Second we compare our version and the database version and find the differences. Third we decide to either - 1) merge and save; 2) discard our changes; 3) return to the user with both versions, and ask him to decide.
dgalehouse (7) [Avatar] Offline
#8
Re: Optimistic Locking
> > I do understand what you're saying but I'm
> wondering
> > if the scenario you describe might be better
> > implemented with a pessimistic lock. Let's say
> that
> > instead of getting blocked by an optimistic lock
> the
> > second user retrieves the object from persistent
> > storage a fraction of a second after the first
> user
> > commits the update to the phone number and then
> > updates it. Does that change anything? There may
> only
> > be a couple of seconds difference between these
> two
> > scenarios but in the first case the app would go
> to
> > the trouble of comparing state and determining
> what
> > has changed and in the second case the update
> would
> > occur unimpeded. Two very different results but
> > essentially the same situation. If the business
> rules
> > really do dictate the type of sensitivity you
> > describe, I'm thinking a pessimistic lock may be a
> > better choice?
>
> Pessimistic Locking is certainly an alternative, but
> I am at the moment more interested in Optimistic
> Locking. My original question was about the handling
> of a version mismatch. The book seems to simply
> suggest that we "retry", and I was trying to
> understand what that means, exactly. I guess what we
> should do when we catch the concurrency failure
> exception, is the following: First we load the most
> recent version of the object from the database.
> Second we compare our version and the database
> version and find the differences. Third we decide to
> either - 1) merge and save; 2) discard our changes;
> 3) return to the user with both versions, and ask him
> to decide.

Just curious, in that situation, what happens if the other user has logged off or is otherwise unavailable?
ceracm (113) [Avatar] Offline
#9
Re: Optimistic Locking
I am glad that you liked the book and sorry for the delay in replying.

There are two kinds of scenarios that determine affect how you do locking.

The first kind is described in chapter 12. A request/transaction reads data and then updates it. The optimistic locking mechanism detects if it has changed since it was read. If you retry the transaction then the application re-reads the data and reapplies the change. In the case of the Send Orders use case it does not matter if the orders have changed. If the order can still be sent it will be sent.

What you are asking about is more like the scenario described in chapter 13 where data is read in one transaction and then updated in another. That's where you would use the offline locking pattern. In this scenario retrying the transaction would not make any difference since the data has changed. For example, you load an order from the database and display it to the user. The user cancels the order. if the order has changed since it was displayed it might make sense to tell the user.

I hope this helps.

Chris