views:

352

answers:

2

I have a simple django app to simulate a stock market, users come in and buy/sell. When they choose to trade,

  1. the market price is read, and
  2. based on the buy/sell order the market price is increased/decreased.

I'm not sure how this works in django, but is there a way to make the view atomic? i.e. I'm concerned that user A's actions may read the price but before it's updated because of his order, user B's action reads the price.

Couldn't find a simple, clean solution for this online. Thanks.

A: 

Wrap the DB queries that read and the ones that update in a transaction. The syntax depends on what ORM you are using.

Sinan Taifour
This is incorrect for Postgresql. You can't assume that reading rows in a transaction will lock them.
Glenn Maynard
+1  A: 

This is database transactions, with some notes. All notes for Postgresql; all databases have locking mechanisms but the details are different.

Many databases don't do this level of locking by default, even if you're in a transaction. You need to get an explicit lock on the data.

In Postgresql, you probably want SELECT ... FOR UPDATE, which will lock the returned rows. You need to use FOR UPDATE on every SELECT that wants to block if another user is about to update them.

Unfortunately, there's no way to do a FOR UPDATE in Django's ORM. You'd eitiher need to hack the ORM a bit or use raw SQL, as far as I know. If this is low-performance code and you can afford to serialize all access to the table, you can use a table-level LOCK IN EXCLUSIVE MODE, which will serialize the whole table.

http://www.postgresql.org/docs/current/static/explicit-locking.html

http://www.postgresql.org/docs/current/static/sql-lock.html

http://www.postgresql.org/docs/current/static/sql-select.html

Glenn Maynard
ORMs aren't meant as a replacement for SQL or a way to avoid SQL anyway, so it's fine to handwrite these kinds of things.
Wahnfrieden
No, it's not fine. It's fine to have to do a little direct work--say, adding ".select_mode('FOR UPDATE')" to a QuerySet instead of having a specialized ".select_for_update()" method. It's completely unacceptable to have to write the *entire thing* by hand, or otherwise have to jump hoops, just because you need to add a flag. Django's ORM is still fairly immature at this sort of thing.
Glenn Maynard
(Of course, I'm not talking about complex, specialized queries that are well outside the vocabulary of the ORM. This is nothing of the sort.)
Glenn Maynard
If you do a `SELECT FOR UPDATE`, in a read view, and the user goes away, for how long is the row locked?Also, id would probably be unacceptable UX wise, just because a user has viewed(or shows intent to edit) a Item, locking it so that no body else can edit is going to piss off some users.
uswaretech
Locks last only for the duration of a transaction. Transactions don't last across multiple site views--you're not locking the row when the user views the page and then keeping it locked. (If you want to do things like warn the user if the price has changed since they viewed the page, that's not locking; you store the price they viewed in their session and compare it explicitly. PayPal does this for currency conversions, for example.)
Glenn Maynard