#17415: Creating an object with an explicit primary key value causes the next
creation to fail under PostgreSQL
-------------------------------------+-------------------------------------
     Reporter:  niko@…               |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  contrib.sites        |                  Version:  SVN
     Severity:  Release blocker      |               Resolution:
     Keywords:  site admin create    |             Triage Stage:  Accepted
  db orm                             |      Needs documentation:  0
    Has patch:  0                    |  Patch needs improvement:  0
  Needs tests:  0                    |                    UI/UX:  0
Easy pickings:  0                    |
-------------------------------------+-------------------------------------

Comment (by akaariai):

 There are some concurrency problems when setting the sequence to "largest
 PK + 1". The problem is that you will not see objects created in other
 concurrent transactions, and thus largest PK is not guaranteed to be
 actually the largest PK. This could cause subtle bugs where everything
 seems to work, except in rare concurrent cases where it doesn't. This
 would be very hard to debug. It is worth considering that creating an
 object with autofield PK set should be an error, except if force_insert is
 defined, or in raw mode (that is, in fixture loading). Document that if
 you use force_insert for a model with set autofield PK, then you are
 responsible for resetting the sequence (or do that automatically in
 force_insert case?).

 The underlying problem is that on SQL level, you should not insert your
 own values to a column which gets its value from a sequence. It is OK to
 do this in non-concurrent setting (as in restore from backup, load
 fixtures). You should not expect it to work in concurrent setting. If
 Django automatically resets the sequence, it makes it look like it is OK
 to insert values to a serial field. But it is not. Unless you lock the
 table for inserts. The whole idea of sequences is that they allow
 concurrent inserts to a table without using max(id) + 1, which just
 doesn't work. (SQLite has one transaction at a time, so there are no
 concurrency problems, MySQL probably does some tricks under the hood, so
 that it can get the actual largest id even in concurrent cases).

 Consider this case, with two transactions T1 and T2, and id_seq starting
 from 10. Largest ID in the table is 10:
 {{{
 T1: begin;
 T1: Obj.save() # gets nextval('id_seq') => 11
 T1: Obj.save() # gets nextval('id_seq') => 12
 T2: begin;
 T2: Obj(pk=1).save() # where pk=1 does not exist
 T2: reset_sequence('id_seq'): finds the largest visible id: 10 sets
 sequence to 11.
 T1: commit; T2: commit;
 }}}
 Now you have: largest id in table: 12, id_seq starts from 11.

 Yes, this is a rare condition. But I believe Django should not expose this
 possible concurrency problem. So, I suggest this is fixed by "disallow
 insert into autofield with user defined value, unless it is asked
 explicitly". If reset_sequence is going to be used, then also lock the
 table (reset sequence should probably do this in any case...).

-- 
Ticket URL: <https://code.djangoproject.com/ticket/17415#comment:9>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

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

Reply via email to