Thread: Sequences in transaction context

Sequences in transaction context

From
"Erik Pearson"
Date:
I searched through mailing list archives but was unable to find full
coverage of this question -- my apologies if this is a reposted question.

As in the FAQ, I am trying to retrieve the value of a sequence value from a
newly inserted row.  So, first I call something like:

    insert into foobar (foo, bar)
    values (nextval('foobar_foo_seq'), 'whatever');

Then, I want to retrieve the value that generated from the sequence and
inserted into the table, so I use a call to currval:

    insert into foobar_rel_table(foo_fk, baz)
    values (currval('foobar_foo_seq', 'something else');

This is (one of the methods that is) prescribed in the FAQ.  However, I'm
concerned that another transaction attempting to insert into the same table
might make a call to nextval('foobar_foo_seq') between the two operations
above.  This would mean that my second statement would use the wrong value
from the sequence.

I've tested this scenario with different transaction isolation levels, and
it appears that any state changes to sequences become immediately visible to
other transactions (obviously, a read-commited type strategy wouldn't work,
however, serializing access to sequences, or explicit locking would solve
this problem).

Has anyone else come across this problem, and is there a workaround?  If
not, are there any alternate suggestions for generating a PK on insert and
immediately retrieving it that is free from concurrency issues?

Any help would be appreciated.  Thanks!  -- Erik

--
Erik Pearson
erik@cariboulake.com
http://www.cariboulake.com






Re: Sequences in transaction context

From
Doug McNaught
Date:
"Erik Pearson" <erik@cariboulake.com> writes:

> I searched through mailing list archives but was unable to find full
> coverage of this question -- my apologies if this is a reposted question.
>
> As in the FAQ, I am trying to retrieve the value of a sequence value from a
> newly inserted row.  So, first I call something like:
>
>     insert into foobar (foo, bar)
>     values (nextval('foobar_foo_seq'), 'whatever');
>
> Then, I want to retrieve the value that generated from the sequence and
> inserted into the table, so I use a call to currval:
>
>     insert into foobar_rel_table(foo_fk, baz)
>     values (currval('foobar_foo_seq', 'something else');
>
> This is (one of the methods that is) prescribed in the FAQ.  However, I'm
> concerned that another transaction attempting to insert into the same table
> might make a call to nextval('foobar_foo_seq') between the two operations
> above.  This would mean that my second statement would use the wrong value
> from the sequence.

This does not happen.  I just tested it:

[doug@shaggy doug]$ createdb foo
CREATE DATABASE
[doug@shaggy doug]$ psql foo
Welcome to psql, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help on internal slash commands
       \g or terminate with semicolon to execute query
       \q to quit

foo=# create sequence foo_seq;
CREATE
foo=# select nextval('foo_seq');
 nextval
---------
       1
(1 row)

foo=# select currval('foo_seq');
 currval
---------
       1
(1 row)

At this point, in another window, I do:

foo=# select nextval('foo_seq');
 nextval
---------
       2
(1 row)

Back to the first window:

foo=# select currval('foo_seq');
 currval
---------
       1
(1 row)

Are you not seeing this behavior?  Since you mention the FAQ, question
4.16.3 addresses this very issue in very clear language.

Welcome to MVCC...

-Doug
--
The rain man gave me two cures; he said jump right in,
The first was Texas medicine--the second was just railroad gin,
And like a fool I mixed them, and it strangled up my mind,
Now people just get uglier, and I got no sense of time...          --Dylan

Re: Sequences in transaction context

From
"Eric G. Miller"
Date:
On Tue, Jun 05, 2001 at 09:22:43PM -0500, Erik Pearson wrote:
> I searched through mailing list archives but was unable to find full
> coverage of this question -- my apologies if this is a reposted question.
>
> As in the FAQ, I am trying to retrieve the value of a sequence value from a
> newly inserted row.  So, first I call something like:
>
>     insert into foobar (foo, bar)
>     values (nextval('foobar_foo_seq'), 'whatever');
>
> Then, I want to retrieve the value that generated from the sequence and
> inserted into the table, so I use a call to currval:
>
>     insert into foobar_rel_table(foo_fk, baz)
>     values (currval('foobar_foo_seq', 'something else');
>
> This is (one of the methods that is) prescribed in the FAQ.  However, I'm
> concerned that another transaction attempting to insert into the same table
> might make a call to nextval('foobar_foo_seq') between the two operations
> above.  This would mean that my second statement would use the wrong value
> from the sequence.
>
> I've tested this scenario with different transaction isolation levels, and
> it appears that any state changes to sequences become immediately visible to
> other transactions (obviously, a read-commited type strategy wouldn't work,
> however, serializing access to sequences, or explicit locking would solve
> this problem).
>
> Has anyone else come across this problem, and is there a workaround?  If
> not, are there any alternate suggestions for generating a PK on insert and
> immediately retrieving it that is free from concurrency issues?

AFAIK, postgresql uses some magic so the currval() call always returns
the last sequence number used by the current process.  This is why
currval() will generate an error in a new session if nextval() hasn't been
called for the given sequence.

--
Eric G. Miller <egm2@jps.net>

Re: Sequences in transaction context

From
Stephan Szabo
Date:
On Tue, 5 Jun 2001, Erik Pearson wrote:

> I searched through mailing list archives but was unable to find full
> coverage of this question -- my apologies if this is a reposted question.
>
> As in the FAQ, I am trying to retrieve the value of a sequence value from a
> newly inserted row.  So, first I call something like:
>
>     insert into foobar (foo, bar)
>     values (nextval('foobar_foo_seq'), 'whatever');
>
> Then, I want to retrieve the value that generated from the sequence and
> inserted into the table, so I use a call to currval:
>
>     insert into foobar_rel_table(foo_fk, baz)
>     values (currval('foobar_foo_seq', 'something else');
>
> This is (one of the methods that is) prescribed in the FAQ.  However, I'm
> concerned that another transaction attempting to insert into the same table
> might make a call to nextval('foobar_foo_seq') between the two operations
> above.  This would mean that my second statement would use the wrong value
> from the sequence.

No, because currval should return the value of the sequence last given to
your session.  If it doesn't that's a problem (and not one I've noticed
reported before).

> I've tested this scenario with different transaction isolation levels, and
> it appears that any state changes to sequences become immediately visible to
> other transactions (obviously, a read-commited type strategy wouldn't work,
> however, serializing access to sequences, or explicit locking would solve
> this problem).

I'm not sure how you're testing this precisely, but none of the tests I've
tried has ever given this kind of result.  Can you give a particular
sequence of events in multiple transactions you've done that's given you
the wrong currval results?


RE: Sequences in transaction context

From
"Erik Pearson"
Date:
Thanks for the reply!  I'm afraid I didn't test this thoroughly.  I noticed
that nextval changed the sequence for all transactions, and assumed that
currval simply translated to the (nextval() - 1), which it does not, as I
now understand it.  I was also unclear on the FAQ -- I guess I thought that
"race condition" referred to concurrent access to the underlying store,
either delivering duplicate values or something.

One last followup question -- what's MVCC?

  -- Erik

> -----Original Message-----
> From: doug@belphigor.mcnaught.org [mailto:doug@belphigor.mcnaught.org]On
> Behalf Of Doug McNaught
> Sent: Tuesday, June 05, 2001 10:14 PM
> To: Erik Pearson
> Cc: pgsql-general@postgresql.org
> Subject: Re: [GENERAL] Sequences in transaction context
>
>
> "Erik Pearson" <erik@cariboulake.com> writes:
>
> > I searched through mailing list archives but was unable to find full
> > coverage of this question -- my apologies if this is a reposted
> question.
> >
> > As in the FAQ, I am trying to retrieve the value of a sequence
> value from a
> > newly inserted row.  So, first I call something like:
> >
> >     insert into foobar (foo, bar)
> >     values (nextval('foobar_foo_seq'), 'whatever');
> >
> > Then, I want to retrieve the value that generated from the sequence and
> > inserted into the table, so I use a call to currval:
> >
> >     insert into foobar_rel_table(foo_fk, baz)
> >     values (currval('foobar_foo_seq', 'something else');
> >
> > This is (one of the methods that is) prescribed in the FAQ.
> However, I'm
> > concerned that another transaction attempting to insert into
> the same table
> > might make a call to nextval('foobar_foo_seq') between the two
> operations
> > above.  This would mean that my second statement would use the
> wrong value
> > from the sequence.
>
> This does not happen.  I just tested it:
>
> [doug@shaggy doug]$ createdb foo
> CREATE DATABASE
> [doug@shaggy doug]$ psql foo
> Welcome to psql, the PostgreSQL interactive terminal.
>
> Type:  \copyright for distribution terms
>        \h for help with SQL commands
>        \? for help on internal slash commands
>        \g or terminate with semicolon to execute query
>        \q to quit
>
> foo=# create sequence foo_seq;
> CREATE
> foo=# select nextval('foo_seq');
>  nextval
> ---------
>        1
> (1 row)
>
> foo=# select currval('foo_seq');
>  currval
> ---------
>        1
> (1 row)
>
> At this point, in another window, I do:
>
> foo=# select nextval('foo_seq');
>  nextval
> ---------
>        2
> (1 row)
>
> Back to the first window:
>
> foo=# select currval('foo_seq');
>  currval
> ---------
>        1
> (1 row)
>
> Are you not seeing this behavior?  Since you mention the FAQ, question
> 4.16.3 addresses this very issue in very clear language.
>
> Welcome to MVCC...
>
> -Doug
> --
> The rain man gave me two cures; he said jump right in,
> The first was Texas medicine--the second was just railroad gin,
> And like a fool I mixed them, and it strangled up my mind,
> Now people just get uglier, and I got no sense of time...          --Dylan
>


Re: Sequences in transaction context

From
Neil Conway
Date:
On Tue, Jun 05, 2001 at 10:33:08PM -0500, Erik Pearson wrote:
> One last followup question -- what's MVCC?

http://www.postgresql.org/idocs/index.php?mvcc.html

Cheers,

Neil


Re: Sequences in transaction context

From
Doug McNaught
Date:
"Erik Pearson" <erik@cariboulake.com> writes:

> Thanks for the reply!  I'm afraid I didn't test this thoroughly.  I noticed
> that nextval changed the sequence for all transactions, and assumed that
> currval simply translated to the (nextval() - 1), which it does not, as I
> now understand it.  I was also unclear on the FAQ -- I guess I thought that
> "race condition" referred to concurrent access to the underlying store,
> either delivering duplicate values or something.
>
> One last followup question -- what's MVCC?

Multi-Version Concurrency Control.  See:

http://postgresql.crimelabs.net/users-lounge/docs/7.1/postgres/mvcc.html

The currval/nextval stuff actually isn't strict MVCC (since it doesn't
require that the usages be inside a transaction) but it's the same
concept.

-Doug
--
The rain man gave me two cures; he said jump right in,
The first was Texas medicine--the second was just railroad gin,
And like a fool I mixed them, and it strangled up my mind,
Now people just get uglier, and I got no sense of time...          --Dylan

Re: Sequences in transaction context

From
Tom Lane
Date:
"Erik Pearson" <erik@cariboulake.com> writes:
> Thanks for the reply!  I'm afraid I didn't test this thoroughly.  I noticed
> that nextval changed the sequence for all transactions, and assumed that
> currval simply translated to the (nextval() - 1), which it does not, as I
> now understand it.

No.  Each backend process caches the last nextval() result it got for
every sequence object it's ever nextval'd, and this data is what
currval() consults.  That's the only way to ensure cross-process
consistency.  What you see in currval is the last value you obtained,
regardless of what anyone else is doing.

> One last followup question -- what's MVCC?

Multi-version concurrency control.  See
http://www.ca.postgresql.org/users-lounge/docs/7.1/postgres/mvcc.html
or my slides on Postgres' transaction model at
http://www.ca.postgresql.org/osdn/index.html

            regards, tom lane