On 4 mars 2013, at 01:07, Shai Berger <[email protected]> wrote: >> On 1 mars 2013, at 13:48, Aymeric Augustin >> <[email protected]> wrote: >> >> I'd like to add an @atomic decorator that: >> - Really guarantees atomicity, by raising an exception if you attempt >> commit within an atomic block, > > Fragility alert: databases commit implicitly. Most notably, Oracle and MySql > for every DDL command.
That a good point. I hadn't considered DDL. > It is true that the commands that trigger this are not usually found in > normally-executed application > code. However, since the proposal is all about running in autocommit mode > unless a transaction > was started explicitly, it should be noted that once such a command is > executed, it does not > just break the transaction in the middle, it renders everything after it > non-transactional. I checked each supported database to determine whether this behavior would be expected. PostgreSQL supports transactional DDL. (The PostgreSQL wiki also indicates that most databases that have a third-party adapter for Django support DDL, which is good news: http://wiki.postgresql.org/wiki/Transactional_DDL_in_PostgreSQL:_A_Competitive_Analysis) SQLite supports transactional DDL — its implementation of transactions with a file lock guarantees that anything can be done in a transaction. The Python sqlite3 module gets in the way again: it will commit on DDL unless autocommit is on: http://bugs.python.org/issue10740. This isn't as braindead as committing on SAVEPOINT, but quite regrettable nonetheless. (I'm not weighting on these bugs because I believe that the default mode of sqlite3 is an aberration that only exists to conform to PEP 249 and cannot be salvaged.) On MySQL, Django will behave exactly like typing commands in the MySQL shell. This behavior shouldn't come as a surprise. See http://dev.mysql.com/doc/refman/5.6/en/implicit-commit.html. Oracle is the only database supported by Django that isn't designed for autocommit by default. Its docs say: "Oracle Database implicitly commits the current transaction before and after every DDL statement." This sentence is obviously written with implicit transaction initiation in mind. This behavior will be surprising for someone used to Oracle's transaction model, but not much more that autocommit in general. It's a logical consequence of "autocommit is on", "@atomic opens a transaction", and "DDL commits the transaction". Finally, a quick search on the web shows that it's considered bad form to rely on DDL to implicitly commit transactions on databases that don't support transactional DDL. > Most DDL commands executed by Django apps, 1.6 and on, should be done through > the upcoming > schema alteration mechanisms -- code under core's control, so things can be > set up correctly > (that is, when such a command is executed under @atomic with a > non-DDL-transactional backend, > it should raise an exception). They just need care. Yes, it's a good idea to add this check when core gets support for DDL commands. > Other than that, strong documentation warnings should be given around the use > of raw sql in transactions. Agreed, this should be documented as a limitation of @atomic. >> - Supports nesting within an existing transaction, with savepoints. > > The use of savepoints in Django apps so far has been very little, as far as I > know. One point > I'm unsure of is the interaction of savepoints with cursors, since querysets > are lazy; so the scenario > I'm worrirf about is, generally speaking, > > @atomic > def main(): > for obj in query_with_more_than_100_objects: > try: > handle(obj) > except Bad: > pass > > @atomic > def handle(obj): > if good(obj): > mark_good(obj) > obj.save() > else: > raise Bad(obj) > > Will the next (database) fetch after an exception is raised get the right > objects? > > My reading of the Postgresql documentation is that it will do the right > thing, not so sure about > the other backends. Django currently doesn't support server side cursors — at least on PostgreSQL, most likely on other databases too. Accessing the first object loads the entire queryset instantly. I suspect it's the database's job to handle this properly (maybe by raising an exception). For example, I know that SQLite is smart enough to deal properly with equivalent scenarios using the C API: it delays committing the transaction until all reads are finished. I don't know how other databases deal with this in general. -- Aymeric. -- You received this message because you are subscribed to the Google Groups "Django developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To post to this group, send email to [email protected]. Visit this group at http://groups.google.com/group/django-developers?hl=en. For more options, visit https://groups.google.com/groups/opt_out.
