Thread: BUG #16964: Quadratic performance degradation of INSERT and ADD CONSTRAINT for intarray/GIST EXCLUDE CONSTRAINT

The following bug has been logged on the website:

Bug reference:      16964
Logged by:          Maxim Boguk
Email address:      maxim.boguk@gmail.com
PostgreSQL version: 13.2
Operating system:   Linux
Description:

I found that combination of EXCLUDE CONSTRAINT with intarray GIST have
quadratic degradation with relation size.
Making it completely useless with tables starting from few thousand rows.

Test dataset can be provided by request (100kb sql file).
Confirmed for 13.2 and 12.* versions.

create extension intarray;
create extension btree_gist;
\i /tmp/2.sql
CREATE TABLE
COPY 4000

structure of 
test=# \d+ test
                                      Table "public.test"
  Column  |   Type    | Collation | Nullable | Default | Storage  | Stats
target | Description 
----------+-----------+-----------+----------+---------+----------+--------------+-------------
 team_id  | integer   |           |          |         | plain    |
    | 
 user_ids | integer[] |           |          |         | extended |
    | 

1 or 2 user_id per array.

Performance results:
ALTER TABLE test
ADD CONSTRAINT unique_user_parties_on_team
      EXCLUDE USING gist (
        team_id WITH =,
        user_ids WITH &&
      );
ALTER TABLE
Time: 911198.957 ms (15:11.199)

(with 2k rows Time: 217885.618 ms (03:37.886))

performance of insert (on 4k rows set):
insert into test  values (10, array[1,2]);
INSERT 0 1
Time: 1204.211 ms (00:01.204)

perf record/report for ADD CONSTRAINT:
  39.65%  postgres  postgres                    [.] pg_qsort
  35.60%  postgres  _int.so                     [.] compASC
   3.68%  postgres  postgres                    [.] swapfunc
   3.26%  postgres  _int.so                     [.] _int_unique
   3.15%  postgres  _int.so                     [.] g_int_decompress
   2.69%  postgres  libc-2.31.so                [.] 0x000000000018ead1
   1.65%  postgres  _int.so                     [.] inner_int_union

perf record/report for INSERT:
  39.32%  postgres  postgres                    [.] pg_qsort
  36.08%  postgres  _int.so                     [.] compASC
   3.18%  postgres  postgres                    [.] swapfunc

It look like somewhat broken for me (especially for so simple use
case/common scenario).

--
Maxim Boguk
Senior Postgresql DBA
https://dataegret.com/


PG Bug reporting form <noreply@postgresql.org> writes:
> I found that combination of EXCLUDE CONSTRAINT with intarray GIST have
> quadratic degradation with relation size.

Hm, I couldn't reproduce any such problem using this data set:

regression=# create table test (team_id int, user_ids int[]);
CREATE TABLE
regression=# insert into test select i, array[i*2,i*2+1] from generate_series(1,4096) i;
INSERT 0 4096
regression=# create extension btree_gist ;
CREATE EXTENSION
regression=# create extension intarray ;
CREATE EXTENSION
Time: 19.077 ms
regression=# ALTER TABLE test
ADD CONSTRAINT unique_user_parties_on_team
      EXCLUDE USING gist (
        team_id WITH =,
        user_ids WITH &&
      );
ALTER TABLE
Time: 459.005 ms
regression=# insert into test  values (10, array[1,2]);
INSERT 0 1
Time: 1.086 ms

            regards, tom lane





On Thu, Apr 15, 2021 at 7:17 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
PG Bug reporting form <noreply@postgresql.org> writes:
> I found that combination of EXCLUDE CONSTRAINT with intarray GIST have
> quadratic degradation with relation size.

Hm, I couldn't reproduce any such problem using this data set:

regression=# create table test (team_id int, user_ids int[]);
CREATE TABLE
regression=# insert into test select i, array[i*2,i*2+1] from generate_series(1,4096) i;
INSERT 0 4096
regression=# create extension btree_gist ;
CREATE EXTENSION
regression=# create extension intarray ;
CREATE EXTENSION
Time: 19.077 ms
regression=# ALTER TABLE test
ADD CONSTRAINT unique_user_parties_on_team
      EXCLUDE USING gist (
        team_id WITH =,
        user_ids WITH &&
      );
ALTER TABLE
Time: 459.005 ms
regression=# insert into test  values (10, array[1,2]);
INSERT 0 1
Time: 1.086 ms

                        regards, tom lane

Hi Tom,

Please see my dataset (2000 rows)
I found an issue actually not related to the btree_gist at all...

Only intarray_gist is enough:
\i test_table_plain_dump_no_constraint.sql
create extension intarray;
\timing on

ALTER TABLE test
ADD CONSTRAINT unique_user_parties
      EXCLUDE USING gist (
        user_ids WITH &&
);


My laptop requires at least 5 minutes to finish it.
Inserts also awfully slow.

--
Maxim Boguk
Senior Postgresql DBA
https://dataegret.com/

Phone RU: +7  985 433 0000
Phone UA: +380 99 143 0000
Phone AU: +61  45 218 5678

LinkedIn: http://www.linkedin.com/pub/maksym-boguk/80/b99/b1b
Skype: maxim.boguk

"Доктор, вы мне советовали так не делать, но почему мне по-прежнему больно когда я так делаю ещё раз?"

Attachment


Hi Tom,

Please see my dataset (2000 rows)
I found an issue actually not related to the btree_gist at all...

Only intarray_gist is enough:
\i test_table_plain_dump_no_constraint.sql
create extension intarray;
\timing on

ALTER TABLE test
ADD CONSTRAINT unique_user_parties
      EXCLUDE USING gist (
        user_ids WITH &&
);


My laptop requires at least 5 minutes to finish it.
Inserts also awfully slow.

After future research I found that
test=# create index test_idx on test using gist(user_ids gist__int_ops);
CREATE INDEX
Time: 200375.964 ms (03:20.376)
test=# create index test1_idx on test using gist(user_ids gist__intbig_ops);
CREATE INDEX
Time: 86.798 ms

have few orders of magnitude difference in runtime...

So I tried
test=# ALTER TABLE test
ADD CONSTRAINT unique_user_parties
      EXCLUDE USING gist (
        user_ids gist__intbig_ops WITH &&                
        );
ALTER TABLE
Time: 172.176 ms

With work without any performance issues.

So I got bitten by gist__int_ops (used by default) of intarray again.
I yet to see any realistic use case when gist__int_ops provide any performance gain over gist__intbig_ops.



--
Maxim Boguk
Senior Postgresql DBA
https://dataegret.com/

Phone RU: +7  985 433 0000
Phone UA: +380 99 143 0000
Phone AU: +61  45 218 5678

LinkedIn: http://www.linkedin.com/pub/maksym-boguk/80/b99/b1b
Skype: maxim.boguk

"Доктор, вы мне советовали так не делать, но почему мне по-прежнему больно когда я так делаю ещё раз?"