Hi Michael,

On Jan 30, 6:25 pm, Michael Hipp <[EMAIL PROTECTED]> wrote:
> James Bennett wrote:
> > On Jan 30, 2008 9:18 AM, Michael Hipp <[EMAIL PROTECTED]> wrote:
> >> Does Django have any built-in way to handle or prevent simultaneous,
> >> incompatible edits to a database record?
>
> > No, that's what your database's concurrency handling is for.
>
> Ok, I probably didn't explain well enough...
>
> I'm using PostgreSQL so concurrency isn't a problem.
>
> What is a problem is that user A loads a record and begins making changes.
> Then user B loads the same record and begins making different changes. One of
> them saves it first followed by the other and the only trace that's left is
> what was saved by the one that got there last.
>
> No real issue at the database level. PG doesn't care. But possibly a very big
> issue at the application level (especially if each of the saves also modified
> related tables based on the different inputs of each user) and the application
> level of each could not know about the other so opportunities to prevent this
> disaster are few at the app level.

The way in which you handle the application-level transactions that
span across a set of requests it entirely up to you :-) Thinking of
typical scenarios you could either synchronize data in real time
(something like google docs), synchronize on save (some kind of diff
and merge), warn users before they start editing a record which is
already opened for edit by another user (like in MoinMoin) or just
overwrite or ignore changes (like in all bad-user-experience-apps).


The third option seems quite reasonable and its also not very hard to
implement. All what you will need is a (generic) registry that will
hold information about checked out objects (rows, records, documents -
whatever the naming convention is) and some locking logic.

The registry could be simply a model with three fields: [generic]
model foreign key, user foreign key, and a checkout time, with unique
constraint on the model foreign key. If you don't need to track
different models then a "classic" foreign key instead of a generic
contenttypes one should be just fine.

The functions that will provide a locking interface should be atomic
(that's the place where the database level transactions come into
play). A typical pattern is to have at least an acquire (lock) and a
release (unlock) routines, plus a couple of additional helpers for the
typical use cases:

* acquire_lock(object, user) - creates an entry/replaces expired one
in the registry, allows for re-entrant locking (the same lock can be
obtained many times by the user that already possess it without
raising an error), raises an exception if an object is already locked
by another user and the lock period hasn't expired.

* release_lock(object, user) - removes lock from the registry

* is_locked(object, user) - checks if a valid object lock entry exists

Cyclic removal of expired entries from the registry should not be
necessary unless you count for every byte or need to reduce number of
queries (in a typical case they will consume considerably less space
than the other models' data).

After setting this up you should have a pretty robust mechanism for
controlling parallel access attempts to database records/objects. Just
lock the object on edit, release the lock on save and issue a warning
on concurrent edit attempts.

> I've seen some table layouts that included a column like "last_edit_serial" so
> that a steadily incrementing serial could provide a hint to the app level that
> something had changed since the record was loaded.

Only when you want to notify someone that his effort has just been
cancelled because someone else has corrected a typo in the meantime
and saving changes is not permitted ;-)

> But I don't see Django adding any such columns and was wondering if a
> different mechanism was in effect. Or if some other technique was widely used
> among Django users.

What makes me wonder even more is the fact how fantastically people
seem to get away without all this stuff :-)


Hope that helps,

f

--
http://filipwasilewski.pl

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to