From 40858417c7fe2cfef0609e2e30e6f910fe25276f Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Fri, 21 Feb 2025 12:11:07 -0800
Subject: [PATCH v36 8/8] Stress test verify_gin() using pgbench

Add a tap test which inserts, updates, deletes, and checks in
parallel.  Like all pgbench based tap tests, this test contains race
conditions between the operations, so Your Mileage May Vary.  For
me, on my laptop, I got failures like:

	index "ginidx" has wrong tuple order on entry tree page

which I have not yet investigated.  The test is included here for
anybody interested in debugging this failure.
---
 contrib/amcheck/t/006_gin_concurrency.pl | 196 +++++++++++++++++++++++
 1 file changed, 196 insertions(+)
 create mode 100644 contrib/amcheck/t/006_gin_concurrency.pl

diff --git a/contrib/amcheck/t/006_gin_concurrency.pl b/contrib/amcheck/t/006_gin_concurrency.pl
new file mode 100644
index 00000000000..afc67940d4d
--- /dev/null
+++ b/contrib/amcheck/t/006_gin_concurrency.pl
@@ -0,0 +1,196 @@
+
+# Copyright (c) 2021-2025, PostgreSQL Global Development Group
+
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+
+use Test::More;
+
+my $node;
+
+#
+# Test set-up
+#
+$node = PostgreSQL::Test::Cluster->new('test');
+$node->init;
+$node->append_conf('postgresql.conf',
+	'lock_timeout = ' . (1000 * $PostgreSQL::Test::Utils::timeout_default));
+$node->start;
+$node->safe_psql('postgres', q(CREATE EXTENSION amcheck));
+$node->safe_psql('postgres', q(CREATE TABLE tbl(i integer[], j jsonb, k jsonb)));
+$node->safe_psql('postgres', q(CREATE INDEX ginidx ON tbl USING gin(i, j, k)));
+$node->safe_psql('postgres', q(CREATE TABLE jsondata (i serial, j jsonb)));
+$node->safe_psql('postgres', q(INSERT INTO jsondata (j) VALUES
+	('1'),
+	('91'),
+	('[5]'),
+	('true'),
+	('"zxI"'),
+	('[1, 7]'),
+	('["", 4]'),
+	('"utDFBz"'),
+	('[[9], ""]'),
+	('"eCvxKPML"'),
+	('["1VMQNQM"]'),
+	('{"": "562c"}'),
+	('[58, 8, null]'),
+	('{"": {"": 62}}'),
+	('["", 6, 19, ""]'),
+	('{"ddfWTQ": true}'),
+	('["", 734.2, 9, 5]'),
+	('"GMV27mjtuuqmlltw"'),
+	('{"dabe": -5, "": 6}'),
+	('"hgihykirQGIYTcCA30"'),
+	('[9, {"Utrn": -6}, ""]'),
+	('"BJTZUMST1_WWEgyqgka_"'),
+	('["", -4, "", [-2], -47]'),
+	('{"": [3], "": {"": "y"}}'),
+	('{"myuijj": "YUWIUZXXLGS"}'),
+	('{"3": false, "C": "1sHTX"}'),
+	('"ZGUORVDE_ACF1QXJ_hipgwrks"'),
+	('{"072": [3, -4], "oh": "eL"}'),
+	('[{"de": 9, "JWHPMRZJW": [0]}]'),
+	('"EACJUZEBAFFBEE6706SZLWVGO635"'),
+	('["P", {"TZW": [""]}, {"": [0]}]'),
+	('{"": -6, "YMb": -22, "__": [""]}'),
+	('{"659": [8], "bfc": [0], "V": ""}'),
+	('{"8776": "1tryl", "Q": 2, "": 4.6}'),
+	('[[1], "", 9, 0, [1, 0], -1, 0, "C"]'),
+	('"635321pnpjlfFzhGTIYP9265iA_19D8260"'),
+	('"klmxsoCFDtzxrhotsqlnmvmzlcbdde34twj"'),
+	('"GZSXSZVS19ecbe_ZJJED0379c1j9_GSU9167"'),
+	('{"F18s": {"": -84194}, "ececab2": [""]}'),
+	('["", {"SVAvgg": "Q"}, 1, 9, "gypy", [1]]'),
+	('[[""], {"": 5}, "GVZGGVGSWM", 2, ["", 8]]'),
+	('{"V": 8, "TPNL": [826, null], "4": -9.729}'),
+	('{"HTJP_DAptxn6": 9, "": "r", "hji4124": ""}'),
+	('[1, ["9", 5, 6, ""], {"": "", "": "efb"}, 7]'),
+	('{"": 6, "1251e_cajrgkyzuxBEDM017444EFD": 548}'),
+	('{"853": -60, "TGLUG_jxmrggv": null, "pjx": ""}'),
+	('[0, "wsgnnvCfJVV_KOMLVXOUIS9FIQLPXXBbbaohjrpj"]'),
+	('"nizvkl36908OLW22ecbdeEBMHMiCEEACcikwkjpmu30X_m"'),
+	('{"bD24eeVZWY": 1, "Bt": 9, "": 6052, "FT": ["h"]}'),
+	('"CDBnouyzlAMSHJCtguxxizpzgkNYfaNLURVITNLYVPSNLYNy"'),
+	('{"d": [[4, "N"], null, 6, true], "1PKV": 6, "9": 6}'),
+	('[-7326, [83, 55], -63, [0, {"": 1}], {"ri0": false}]'),
+	('{"": 117.38, "FCkx3608szztpvjolomzvlyrshyvrgz": -4.2}'),
+	('["", 8, {"WXHNG": {"6": 4}}, [null], 7, 2, "", 299, 6]'),
+	('[[-992.2, "TPm", "", "cedeff79BD8", "t", [1]], 0, [-7]]'),
+	('[9, 34, ["LONuyiYGQZ"], [7, 88], ["c"], 1, 6, "", [[2]]]'),
+	('[20, 5, null, "eLHTXRWNV", 8, ["pnpvrum", -3], "FINY", 3]'),
+	('[{"": "", "b": 2, "d": "egu"}, "aPNK", 2, 9, {"": -79946}]'),
+	('[1, {"769": 9}, 5, 9821, 22, 0, 2.7, 5, 4, 191, 54.599, 24]'),
+	('["c", 77, "b_0lplvHJNLMxw", "VN76dhFadaafadfe5dfbco", false]'),
+	('"TYIHXebbPK_86QMP_199bEEIS__8205986vdC_CFAEFBFCEFCJQRHYoqztv"'),
+	('"cdmxxxzrhtxpwuyrxinmhb5577NSPHIHMTPQYTXSUVVGJPUUMCBEDb_1569e"'),
+	('[[5, null, "C"], "ORNR", "mnCb", 1, -800, "6953", ["K", 0], ""]'),
+	('"SSKLTHJxjxywwquhiwsde353eCIJJjkyvn9946c2cdVadcboiyZFAYMHJWGMMT"'),
+	('"5185__D5AtvhizvmEVceF3jxtghlCF0789_owmsztJHRMOJ7rlowxqq51XLXJbF"'),
+	('{"D": 565206, "xupqtmfedff": "ZGJN9", "9": 1, "glzv": -47, "": -8}'),
+	('{"": 9, "": {"": [null], "ROP": 842}, "": ["5FFD", 7, 5, 1, 94, 1]}'),
+	('{"JLn": ["8s"], "": "_ahxizrzhivyzvhr", "XSAt": 5, "P": 2838, "": 5}'),
+	('[51, 3, {"": 9, "": -9, "": [[6]]}, 7, 7, {"": 0}, "TXLQL", 7.6, [7]]'),
+	('[-38.7, "kre40", 5, {"": null}, "tvuv", 8, "", "", "uizygprwwvh", "1"]'),
+	('"z934377_nxmzjnuqglgyukjteefeihjyot1irkvwnnrqinptlpzwjgmkjbQMUVxxwvbdz"'),
+	('[165.9, "dAFD_60JQPYbafh", false, {"": 6, "": "fcfd"}, [[2], "c"], 4, 2]'),
+	('"ffHOOPVSSACDqiyeecTNWJMWPNRXU283aHRXNUNZZZQPUGYSQTTQXQVJM5eeafcIPGIHcac"'),
+	('[2, 8, -53, {"": 5}, "F9", 8, "SGUJPNVI", "7OLOZH", 9.84, {"": 6}, 207, 6]'),
+	('"xqmqmyljhq__ZGWJVNefagsxrsktruhmlinhxloupuVQW0804901NKGGMNNSYYXWQOosz8938"'),
+	('{"FEoLfaab1160167": {"L": [42, 0]}, "938": "FCCUPGYYYMQSQVZJKM", "knqmk": 2}'),
+	('"0igyurmOMSXIYHSZQEAcxlvgqdxkhwtrbaabfaaMC138Z_BDRLrythpi30_MPRXMTOILRLswmoy"'),
+	('"1129BBCABFFAACA9VGVKipnwohaccc9TSIMTOQKHmcGYVeFE_PWKLHmpyj60137672qugtsstugg"'),
+	('"D3BDA069074174vx48A37IVHWVXLUP9382542ypsl1465pixtryzCBgrkkhrvCC_BDDFatkyXHLIe"'),
+	('[{"esx7": -53, "ec60834YGVMYoXAAvgxmmqnojyzmiklhdovFipl": 2, "os": 66433}, 9.13]'),
+	('{"": ["", 4, null, 5, null], "": "3", "5_GMMHTIhPB_F_vsebc1": "Er", "GY": 121.32}'),
+	('["krTVPYDEd", 5, 8, [6, -6], [[-9], 3340, [[""]]], "", 5, [6, true], 3, "", 1, ""]'),
+	('{"rBNPKN8446080wruOLeceaCBDCKWNUYYMONSJUlCDFExr": {"": "EE0", "6826": 5, "": 7496}}'),
+	('[3, {"": -8}, "101dboMVSNKZLVPITLHLPorwwuxxjmjsh", "", "LSQPRVYKWVYK945imrh", 4, 51]'),
+	('[["HY6"], "", "bcdB", [2, [85, 1], 3, 3, 3, [8]], "", ["_m"], "2", -33, 8, 3, "_xwj"]'),
+	('["", 0, -3.7, 8, false, null, {"": 5}, 9, "06FccxFcdb283bbZGGVRSMWLJH2_PBAFpwtkbceto"]'),
+	('[52, "", -39, -7, [1], "c", {"": 9, "": 45528, "G": {"": 7}}, 3, false, 0, "EB", 8, -6]'),
+	('"qzrkvrlG78CCCEBCptzwwok808805243QXVSYed3efZSKLSNXPxhrS357KJMWSKgrfcFFDFDWKSXJJSIJ_yqJu"'),
+	('[43, 8, {"": ""}, "uwtv__HURKGJLGGPPW", 9, 66, "yqrvghxuw", {"J": false}, false, 2, 0, 4]'),
+	('[{"UVL": 7, "": 1}, false, [6, "H"], "boxlgqgm", 3, "znhm", [true], 0, ["e", 3.7], 9, 9.4]'),
+	('{"825634870117somzqw": 1, "": [5], "gYH": "_XT", "b22412631709RZP": 3, "": "", "FDB": [""]}'),
+	('[8, ["_bae"], "", "WN", 80, {"o": 2, "aff": 16}, false, true, 4, 6, {"nutzkzikolsxZRQ": 30}]'),
+	('["588BD9c_xzsn", {"k": 0, "_Ecezlkslrwvjpwrukiqzl": 3, "Ej": "4"}, "TUXwghn1dTNRXJZpswmD", 5]'),
+	('[{"dC": 7}, {"": 1, "4": 41, "": "", "": "adKS"}, {"": "ypv"}, 6, 9, 2, [-61.46], [1, 3.9], 2]'),
+	('{"8": 8, "": -364, "855": -238.1, "zj": 9, "SNHJG413": 3, "UMNVI73": [60, 0], "iwvqse": -1.833}'),
+	('"VTUKMLZKQPHIEniCFZ_cjrhvspxzulvxhqykjzmrw89OGOGISWdcrvpOPLOFALGK809896999xzqnkm63254_xrmcfcedb"'),
+	('["", "USNQbcexyFDCdBAFWJIphloxwytplyZZR008400FmoiYXVYOHVGV79795644463Aug_aeoDDEjzoziisxoykuijhz"]'),
+	('{"": 1, "5abB58gXVQVTTMWU3jSHXMMNV": "", "nv": 934, "kjsnhtj": 8, "": [{"xm": [71, 425]}], "": -9}'),
+	('"__oliqCcbwwyqmtECsqivplcb1NTMOQRZTYRJONOIPWNHKWLJRIHKROMJNZLNGTTKRcedebccdbMTQXSzhynxmllqxuhnxBA_"'),
+	('["thgACBWGNGMkFFEA", [0, -1349, {"18": "RM", "F3": 6, "dP": "_AF"}, 64, 0, {"f": [8]}], 5, [[0]], 2]')
+));
+
+#
+# Stress gin with pgbench.
+#
+# Modify the table data, and hence the index data, from multiple process
+# while from other processes run the index checking code.  This should,
+# if the index is large enough, result in the checks performing across
+# concurrent page splits.
+#
+$node->pgbench(
+	'--no-vacuum --client=20 --transactions=5000',
+	0,
+	[qr{actually processed}],
+	[qr{^$}],
+	'concurrent DML and index checking',
+	{
+		'006_gin_concurrency_insert_1' => q(
+			INSERT INTO tbl (i, j, k)
+				(SELECT ARRAY[x.i, y.i, random(0,100000), random(0,100000)], x.j, y.j
+					FROM jsondata x, jsondata y
+					WHERE x.i = random(1,100)
+					  AND y.i = random(1,100)
+				)
+		  ),
+		'006_gin_concurrency_insert_2' => q(
+			INSERT INTO tbl (i, j, k)
+				(SELECT gs.i, j.j, j.j || j.j
+					FROM jsondata j,
+						 (SELECT array_agg(gs) AS i FROM generate_series(random(0,100), random(101,200)) gs) gs
+					WHERE j.i = random(1,100)
+				)
+		  ),
+		'006_gin_concurrency_insert_nulls' => q(
+			INSERT INTO tbl (i, j, k) VALUES
+				(null,               null, null),
+				(null,               null, '[]'),
+				(null,               '[]', null),
+				(ARRAY[]::INTEGER[], null, null),
+				(null,               '[]', '[]'),
+				(ARRAY[]::INTEGER[], '[]', null),
+				(ARRAY[]::INTEGER[], '[]', '[]')
+		  ),
+		'006_gin_concurrency_update_i' => q(
+			UPDATE tbl
+				SET i = (SELECT i || i FROM tbl ORDER BY random() LIMIT 1)
+				WHERE j = (SELECT j FROM tbl ORDER BY random() LIMIT 1);
+		),
+		'006_gin_concurrency_update_j' => q(
+			UPDATE tbl
+				SET j = (SELECT j || j FROM tbl ORDER BY random() LIMIT 1)
+				WHERE k = (SELECT k FROM tbl ORDER BY random() LIMIT 1);
+		),
+		'006_gin_concurrency_update_k' => q(
+			UPDATE tbl
+				SET k = (SELECT k || k FROM tbl ORDER BY random() LIMIT 1)
+				WHERE i = (SELECT i FROM tbl ORDER BY random() LIMIT 1);
+		),
+		'006_gin_concurrency_delete' => q(
+			DELETE FROM tbl
+				WHERE random(1,5) = 3;
+		),
+		'006_gin_concurrency_gin_index_check' => q(
+				SELECT gin_index_check('ginidx');
+		)
+	});
+
+$node->stop;
+done_testing();
+
-- 
2.39.3 (Apple Git-145)

