-- merge_fanin: segments of a tier that trigger a merge (default 4, 2 to 32), read at merge time: ALTER INDEX, no REINDEX. CREATE EXTENSION pg_sift; SET enable_seqscan = off; CREATE TABLE mf (id serial, body text) WITH (autovacuum_enabled = off); INSERT INTO mf (body) SELECT 'k' || (i % 7) || ' g' || (i % 1000) || ' common' FROM generate_series(1, 6000) i; -- range CREATE INDEX mf_idx ON mf USING sift (body) WITH (build_segments = 24); SELECT reloptions FROM pg_class WHERE relname = 'mf_idx'; reloptions --------------------- {build_segments=24} (1 row) ALTER INDEX mf_idx SET (merge_fanin = 1); ERROR: value 1 out of bounds for option "merge_fanin" DETAIL: Valid values are between "2" and "32". ALTER INDEX mf_idx SET (merge_fanin = 33); ERROR: value 33 out of bounds for option "merge_fanin" DETAIL: Valid values are between "2" and "32". ALTER INDEX mf_idx SET (merge_fanin = 2); ALTER INDEX mf_idx SET (merge_fanin = 32); ALTER INDEX mf_idx RESET (merge_fanin); -- the default (4) merges 24 segments of a tier in three steps of 8 and leaves 3; 32 waits for 32 of a tier; 2 merges down to one SELECT count(*) AS built FROM sift.segments('mf_idx'); built ------- 24 (1 row) SELECT sift.merge('mf_idx') AS merges_default; merges_default ---------------- 3 (1 row) SELECT count(*) AS segments FROM sift.segments('mf_idx'); segments ---------- 3 (1 row) DROP INDEX mf_idx; CREATE INDEX mf_idx ON mf USING sift (body) WITH (build_segments = 24, merge_fanin = 32); SELECT sift.merge('mf_idx') AS merges_32; merges_32 ----------- 0 (1 row) SELECT count(*) AS segments FROM sift.segments('mf_idx'); segments ---------- 24 (1 row) ALTER INDEX mf_idx SET (merge_fanin = 2); SELECT sift.merge('mf_idx') AS merges_2; merges_2 ---------- 8 (1 row) SELECT count(*) AS segments FROM sift.segments('mf_idx'); segments ---------- 1 (1 row) SELECT count(*) AS hits FROM mf WHERE body @@> 'common'; hits ------ 6000 (1 row) SELECT sift.verify('mf_idx', true); verify -------- (1 row) DROP INDEX mf_idx; -- every fanin in range leaves the index within the policy's bounds: fewer than fanin segments (all of one tier here), and never -- more directory entries than the policy tolerates, greatest(32, 2 * fanin), which is below the 112 where inserters make room CREATE TEMP TABLE sweep (fanin int, built int, merges int, segments int, hits bigint); DO $$ DECLARE f int; m int; s int; b int; h bigint; BEGIN FOR f IN 2..32 LOOP EXECUTE format('CREATE INDEX mf_sweep ON mf USING sift (body) WITH (build_segments = 64, merge_fanin = %s)', f); SELECT count(*) INTO b FROM sift.segments('mf_sweep'); m := sift.merge('mf_sweep'); SELECT count(*) INTO s FROM sift.segments('mf_sweep'); SELECT count(*) INTO h FROM mf WHERE body @@> 'common'; PERFORM sift.verify('mf_sweep', true); INSERT INTO sweep VALUES (f, b, m, s, h); DROP INDEX mf_sweep; END LOOP; END $$; SELECT count(*) AS fanins, bool_and(segments < fanin) AS settled, bool_and(segments <= greatest(32, 2 * fanin)) AS bounded, bool_and(segments < 112) AS room, bool_and(hits = 6000) AS complete, min(built) AS built_min FROM sweep; fanins | settled | bounded | room | complete | built_min --------+---------+---------+------+----------+----------- 31 | t | t | t | t | 39 (1 row) SELECT fanin, built, merges, segments FROM sweep WHERE fanin IN (2, 3, 8, 16, 17, 31, 32) ORDER BY fanin; fanin | built | merges | segments -------+-------+--------+---------- 2 | 39 | 13 | 1 3 | 39 | 8 | 1 8 | 39 | 3 | 1 16 | 39 | 1 | 8 17 | 39 | 1 | 6 31 | 39 | 1 | 1 32 | 39 | 1 | 1 (7 rows) DROP TABLE mf; DROP EXTENSION pg_sift;