Re: Slow alter sequence with PG10.1 - Mailing list pgsql-general

From Laurenz Albe
Subject Re: Slow alter sequence with PG10.1
Date
Msg-id 1516650741.9843.39.camel@cybertec.at
Whole thread Raw
In response to Slow alter sequence with PG10.1  (Michael Krüger <michael@kruegers.email>)
List pgsql-general
Michael Krüger wrote:
> I'm using PG10.1 on CentOS Linux release 7.4.1708 (Core) after upgrading it from PG9.6.6.
> My application heavily uses sequences and requires different increments of sequence numbers,
> e.g. a range of 100, 1000 or 5000 numbers, so it is not possible to set a fixed increment
> on a sequence that can be used by my application.
> 
> With PG10.1 the performance has dropped seriously so that my application becomes unusable.
> After investigating different aspects, I was able to isolate the issue to be related to
> the sequences in Postgres 10.1. 
> 
> Below shows a simple test script showing the problem:
> -- 1) Create a sequence
> CREATE SEQUENCE my_sequence_1 INCREMENT BY 1 MINVALUE 1 NO MAXVALUE START WITH 1 CYCLE;
> 
> -- 2) Create a function that allows to request a number range
> CREATE OR REPLACE FUNCTION multi_nextval(
[...]
>     perform pg_advisory_lock(lock_id);
>     execute 'ALTER SEQUENCE ' || use_seqname || ' INCREMENT BY ' || use_increment::text;
>     reply := nextval(use_seqname);
>     execute 'ALTER SEQUENCE ' || use_seqname || ' INCREMENT BY 1';
>     perform pg_advisory_unlock(lock_id);
[...]

> Further investigation showed that the problem is related to ALTER SEQUENCE... 
> 
> I can't believe that PG10.1 was changed that dramatically without providing
> a workaround or a way to switch to the old PG9.6 performance, at least I
> can't find anything in the documentation. 
> 
> Is this a bug?

See https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=3d79013b970d4cc336c06eb77ed526b44308c03e

  Make ALTER SEQUENCE, including RESTART, fully transactional.

  Previously the changes to the "data" part of the sequence, i.e. the
  one containing the current value, were not transactional, whereas the
  definition, including minimum and maximum value were.  That leads to
  odd behaviour if a schema change is rolled back, with the potential
  that out-of-bound sequence values can be returned.

  To avoid the issue create a new relfilenode fork whenever ALTER
  SEQUENCE is executed, similar to how TRUNCATE ... RESTART IDENTITY
  already is already handled.

This fixed a bug introduced in v10 by this change:
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=1753b1b027035029c2a2a1649065762fafbf63f3

  Add pg_sequence system catalog

  Move sequence metadata (start, increment, etc.) into a proper system
  catalog instead of storing it in the sequence heap object.  This
  separates the metadata from the sequence data.  Sequence metadata is now
  operated on transactionally by DDL commands, whereas previously
  rollbacks of sequence-related DDL commands would be ignored.

Previous to that change, ALTER SEQUENCE modified the values in the
sequence data file in place --- different from an UPDATE to a regular
table, which would write a new table row.

Consequently, the changes ALTER SEQUENCE were not rolled back in
9.6 and before.

After the change, the kinds of ALTER SEQUENCE that changed the values
in pg_sequence were transactional, but ALTER SEQUENCE ... RESTART,
which changes "last_value", which is stored in the data file, was not.

See the example in
https://www.postgresql.org/message-id/20170522154227.nvafbsm62sjpbxvd@alap3.anarazel.de

The solution was to create a new filenode whenever ALTER SEQUENCE is run,
which is the cause for the performance regression.


Now maybe the fix is really not perfect, but what else could be done?
Introducing row versions for sequences is wrong, because all transactions
have to see the same value for a sequence, otherwise it could not
serve its purpose.

I think that having transactional ALTER SEQUENCE is worth the price
of a slowdown for ALTER SEQUENCE.


I'd say that your function abuses ALTER SEQUENCE, and it would be better to
rewrite it.

The best solution if you need a gap-less batch of sequence values is
in my opinion to use a sequence with defined START WITH 10000 INCREMENT BY 10000
and get the starting value for the next batch of 10000 with
SELECT nextval('seq') - 9999.
That will waste some values if you don't need all 10000 values, but it
is very efficient and does not require a lock at all.

Yours,
Laurenz Albe


pgsql-general by date:

Previous
From: "Ivan E. Panchenko"
Date:
Subject: Re: Slow alter sequence with PG10.1
Next
From: Jeff Janes
Date:
Subject: Re: Using random() in update produces same random value for all