need to mimic threading across controllers

Hi,

I'm hoping to make a parent and several of its dependent children
available for updates by multiple users. I want to figure out how to
implement and test the following:

1. A user 'checks out' a parent resource: when seeking to update,
other users are informed that the resource and its dependent children
are currently unavailable for updating.

2. A user 'checks out' a child resource: when seeking to update, other
users are informed that the child is unavailable for updating, though
the parent may be available.

I think a reason I'm stumped here is that I have little deployment
experience. Yet, even so, how would I test the behaviors above,
locally?

Thanks for any comments,

Lille

Presumably you've got a flag of some sort that you set to indicate
that a record in use ? (or perhaps better than a flag, one or both of
locked_since and locked_by fields telling other users who has the
record locked). I don't see any particular difficulties with testing
this

Fred

Hi,

I'm hoping to make a parent and several of its dependent children
available for updates by multiple users. I want to figure out how to
implement and test the following:

1. A user 'checks out' a parent resource: when seeking to update,
other users are informed that the resource and its dependent children
are currently unavailable for updating.

2. A user 'checks out' a child resource: when seeking to update, other
users are informed that the child is unavailable for updating, though
the parent may be available.

In both of these cases, the only time that the user would "know" that the element was locked is when they request it initially, or after a failure to save due to a lock. Have a read of optimistic locking in the Rails Guide. The only issue I can think of here is one of user frustration. Let's say they had just spent 20 minutes writing an update to a record, now they can't save it, and won't ever be able to save it because the starting point for their edit has moved underneath them.

Walter

Thanks gents.

The nice thing about pessimistic locking for me could be that it would
help avoid the user problem to which Walter refers and I wouldn't have
to add columns to all my models, as I would for optimistic locking.

@Fred - I see you've written pretty often on the topic of optimistic
locking. What, to your reckoning, are the minimum changes I'd need to
make to my models & forms to ensure the following:

+ user 1 fails to make any updates to the resource they have obtained
from find
+ user 2 arrives later, finds the object, and should have the ability
to update the resource

Thanks,

Lille

The term you will want to Google for is "race condition". As Fred and
Walter say, there's no huge issue with trying to work around it - but
it can cause frustration if not handled correctly (like some kind of
"draft" save in the event of a conflict).

Thanks gents.

The nice thing about pessimistic locking for me could be that it would
help avoid the user problem to which Walter refers and I wouldn't have
to add columns to all my models, as I would for optimistic locking.

I think you are misusing terminology slightly - pessimistic locking
usually refers to database level locks (ie select ... for update)
which is definitely not appropriate for this sort of scenario.

@Fred - I see you've written pretty often on the topic of optimistic
locking. What, to your reckoning, are the minimum changes I'd need to
make to my models & forms to ensure the following:

+ user 1 fails to make any updates to the resource they have obtained
from find
+ user 2 arrives later, finds the object, and should have the ability
to update the resource

Personally, unless you think you will be able to merge potentially
conflicting changes (as you would with git) the easiest think is to
mark records as locked by a user. You could build in lock expiry - if
it has been locked for over a certain duration, unlock the record
automatically

Fred

Just in case, do you really need that level of locking?

Perhaps you know it, but in web programming "the last edit wins" is a
meaningful rule that allows you to get rid of these controls. If two
people edit the same form, the last received data is the one that
prevails, it doesn't matter who requested the form first. In
particular it does not matter whether that happened in parallel or
sequentially but just immediately. The data of the first guy will be
overwritten anyway, so in practice often it does not matter.

If it does matter in your use case please just disregard this mail.

That's a horrible rule and a great way to lose data. The correct rule is the first edit wins. The last one fails and must refresh his view and try again. This is easily accomplished by using version numbers and failing any update where the version has changed since the initial view.

Nah, I don't think that's right. Versioning may make sense in some scenario
(as my comment implicitly acknowledged), but calling the rule above
"horrible" is denying the fact that it makes perfect sense in a lot of
use cases. Indeed it is the most common way to implement concurrency
handling: doing nothing because last wins is normally fine.

If user U says the state of a model is S, and user W says a
millisecond later that it is T, and the edition made by W does not
depend on the exact previous state of the model, then why you should
not believe W? He said it is T, so let it be T.

From the point of view of user U, he doesn't know whether the requests
were interleaved or sequential, and he does not care normally. All he
knows is that after he took a bit of coffee someone else edited the
same record and now it looks different.

If the edition does depend on the previous state, then you need to put
those extra controls.

Nah, I don't think that's right. Versioning may make sense in some scenario
(as my comment implicitly acknowledged), but calling the rule above
"horrible" is denying the fact that it makes perfect sense in a lot of
use cases.

I called your saying web apps are special and make last in the general rule horrible.

Web apps aren't special, whether first or last edit wins is decided on whether it's acceptable to lose data or not in a given situation regardless of it being a web app or any other kind of app.

Indeed it is the most common way to implement concurrency
handling: doing nothing because last wins is normally fine.

That's not implementing concurrency handling, it's ignoring it.

If user U says the state of a model is S, and user W says a
millisecond later that it is T, and the edition made by W does not
depend on the exact previous state of the model, then why you should
not believe W? He said it is T, so let it be T.

Because you don't know it was a millisecond later, it could have been 10 minutes later because user W sat there looking at the screen forever. User U may have changed many fields on the model while user W just updated one and you've now lost all of the data user U entered effectively rolling back the record.

If that's acceptable, fine, last edit wins, however, for many if not most business type objects, it's not OK.

From the point of view of user U, he doesn't know whether the requests

were interleaved or sequential, and he does not care normally. All he
knows is that after he took a bit of coffee someone else edited the
same record and now it looks different.

Not just different, all of his work was lost because user W updated one unrelated field. User U is now very pissed.

I don't see any data loss necessarily, that depends on your
application. If W says all those fields are fine as they are, I trust
W. I trust the last one. How do you know W is wrong a priori?

The order in which the application sent the forms may be irrelevant
(again, I am using "may"). If W says the state is T, let it be T. He
was in particular saying that it is not S (for S != T if you allow me
the detail :).

It is your application who defines what's a reasonable behavior.

I don't see any data loss necessarily, that depends on your
application.

That's a given.

If W says all those fields are fine as they are, I trust
W. I trust the last one. How do you know W is wrong a priori?

You can't trust W, he never saw the changes the other guy made because he was editing an old copy. W update should fail because the record was changed since he last viewed it and he should be shown the current data and told to attempt his change again on the current data. If you're really fancy, you attempt to merge for him and ask for approval.

The order in which the application sent the forms may be irrelevant
(again, I am using "may"). If W says the state is T, let it be T. He
was in particular saying that it is not S (for S != T if you allow me
the detail :).

State is rarely a single field, T may have 20 fields where U updated 15 of them and W updated just 1 and you're saying it's the perfect default behavior to lose all of U's hard work because W made one change to an unrelated field. That's just crazy *as a default* unless you've determined up front that data loss doesn't matter. You *will lose data*.

It is your application who defines what's a reasonable behavior.

Again, that's a given.