S
S
stepank2011-07-26 12:42:02
Django
stepank, 2011-07-26 12:42:02

ORM and object changes in different places in the code?

Suppose there is some system with a database. There are three entities in this system:
- user (User),
- transaction (Transaction),
- user account (Account).
They are connected in such a way that the user and the transaction refer to the account by the foreign key.
We want to work with this whole thing through some kind of ORM. In my case, this is Django ORM.
Next, let's say we have some account that initially has a balance of 100. We also have a transaction and a user that refer to that account.
Now we do the following:
1. Access the user.account field so that the account object is pulled from the database into one variable:
print user.account
2. Access the transaction.account field so that the account object is pulled from the database into another variable:
print transaction.account
3. Update the user's account and save:

user.account.balance = 0<br/>
user.account.save()

4. Update the account for the transaction (but in a different way) and also save:
transaction.account.balance += 100<br/>
transaction.account.save()

As a result, the database stores information that the balance is 200, because. in paragraph 4, nothing is known about what happened in paragraph 3. And while this result is quite logical, it may be quite unexpected.
Actually, the situation is quite real: points 1 and 2 can be located in different parts of the system and do not know anything about each other.
In this connection, there are a couple of questions:
1. How to avoid such mistakes, except how to be very careful?
2. Maybe this is a purely Django ORM issue, how are things with Java Hibernate for example?
PS: Found a similar discussion on stackoverflow: stackoverflow.com/questions/680320/django-orm-cach...

Answer the question

In order to leave comments, you need to log in

6 answer(s)
O
Ololesha Ololoev, 2011-07-26
@alexeygrigorev

As I understand it, all code must be executed within a single transaction. In Django, this is done using the transaction (from django.db import transaction) decorator. And operation 3 cannot be executed until, say, operation 4 is completed.

U
ultimate_darkness, 2011-07-26
@ultimate_darkness

Regarding the second question. In Hibernate, if all operations occur within the same session (Hibernate Session), then transaction.account and user.accout will return the same entity, so you can safely change its fields in different places in the code.

A
Anatoly, 2011-07-26
@taliban

Maybe I don’t understand something, but apparently ActiveRecord is used and a record like user.account.save () should save the data in the database, right? No matter where it is called from, the save method must save the current changes to the object in the database, otherwise why is it needed at all?

S
Suor, 2011-07-26
@Suor

For one operation, manipulations with the account in two different places should not be done. The problem is the architecture of the code, it's bad. You just need to look at everything together and refactor

D
dborovikov, 2011-08-10
@dborovikov

The only correct solution to your problem is to use select for update. And yes, in order to protect yourself from hard-to-find errors, you should add a field with the version. Even if there is no synchronization, then at least transactions will be rolled back. Well, or use the SERIALIZATION isolation level, which will also roll back the transaction.

K
kmike, 2011-08-11
@kmike

Also think, do you really need the “Account” entity? In fact, as far as I understand, this is just a cache so that the balance from transactions is not recalculated every time. So treat it like a cache - the same approaches to invalidation, etc. Maybe even recalculating the amount on the account each time will turn out to be ok, are you sure you run into this in terms of speed?
And, of course, there should be only one account update point: either some explicit function, or to create a Transaction to recalculate the value in Account, or (imho badly) to create a Transaction to change Account.
select_for_update is in the junga trunk. For an atomic increase in the field, you can use the F-object (but it's better to recalculate everything in its entirety, again imho).

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question