diff --git a/columnar/src/backend/columnar/columnar_metadata.c b/columnar/src/backend/columnar/columnar_metadata.c index 2cdda4fc..7b8a0428 100644 --- a/columnar/src/backend/columnar/columnar_metadata.c +++ b/columnar/src/backend/columnar/columnar_metadata.c @@ -559,7 +559,7 @@ SaveChunkGroups(RelFileNode relfilenode, uint64 stripe, } FinishModifyRelation(modifyState); - table_close(columnarChunkGroup, NoLock); + table_close(columnarChunkGroup, RowExclusiveLock); } @@ -663,7 +663,7 @@ SaveEmptyRowMask(uint64 storageId, uint64 stripeId, } FinishModifyRelation(modifyState); - table_close(columnarRowMask, NoLock); + table_close(columnarRowMask, RowExclusiveLock); return chunkInserted; } @@ -1960,20 +1960,19 @@ DeleteStripeFromColumnarMetadataTable(Oid metadataTableId, ScanKeyData scanKey[2]; ScanKeyInit(&scanKey[0], storageIdAtrrNumber, BTEqualStrategyNumber, F_INT8EQ, UInt64GetDatum(storageId)); - ScanKeyInit(&scanKey[0], stripeIdAttrNumber, BTEqualStrategyNumber, + ScanKeyInit(&scanKey[1], stripeIdAttrNumber, BTEqualStrategyNumber, F_INT8EQ, UInt64GetDatum(stripeId)); - Relation metadataTable = try_relation_open(metadataTableId, AccessShareLock); + Relation metadataTable = try_relation_open(metadataTableId, RowShareLock); if (metadataTable == NULL) { /* extension has been dropped */ return; } - Relation index = index_open(storageIdIndexId, AccessShareLock); - + Relation index = index_open(storageIdIndexId, RowShareLock); SysScanDesc scanDescriptor = systable_beginscan_ordered(metadataTable, index, NULL, - 1, scanKey); + 2, scanKey); ModifyState *modifyState = StartModifyRelation(metadataTable); @@ -1988,8 +1987,8 @@ DeleteStripeFromColumnarMetadataTable(Oid metadataTableId, FinishModifyRelation(modifyState); - index_close(index, AccessShareLock); - table_close(metadataTable, AccessShareLock); + index_close(index, RowShareLock); + table_close(metadataTable, RowShareLock); } diff --git a/columnar/src/backend/columnar/columnar_reader.c b/columnar/src/backend/columnar/columnar_reader.c index 4c9cc0c1..71c99e8d 100644 --- a/columnar/src/backend/columnar/columnar_reader.c +++ b/columnar/src/backend/columnar/columnar_reader.c @@ -444,11 +444,11 @@ ColumnarReadRowByRowNumber(ColumnarReadState *readState, } /* - * ColumnaSetStripeReadState + * ColumnarSetStripeReadState */ bool -ColumnaSetStripeReadState(ColumnarReadState *readState, - StripeMetadata *startStripeMetadata) +ColumnarSetStripeReadState(ColumnarReadState *readState, + StripeMetadata *startStripeMetadata) { if (!ColumnarReadIsCurrentStripe(readState, startStripeMetadata->firstRowNumber)) { diff --git a/columnar/src/backend/columnar/columnar_tableam.c b/columnar/src/backend/columnar/columnar_tableam.c index 7f7a69bc..f5117b9f 100644 --- a/columnar/src/backend/columnar/columnar_tableam.c +++ b/columnar/src/backend/columnar/columnar_tableam.c @@ -40,6 +40,7 @@ #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/predicate.h" +#include "storage/proc.h" #include "storage/procarray.h" #include "storage/smgr.h" #include "tcop/utility.h" @@ -1200,6 +1201,22 @@ TruncateAndCombineColumnarStripes(Relation rel, int elevel) } } + /* + * We need to clear current proces (VACUUUM) status flag here. Why? + * Current process has flag `PROC_IN_VACUUM` which is problematic because + * we write here into metadata heap tables. If concurrent process + * read page in which we inserted metadata tuples these tuples will be considered + * DEAD and will be removed (in problematic scenarion). + * Concurrent process, when assigning RecentXmin will scan all active processes in + * system but will NOT consider this process because of + * `PROC_IN_VACUUM` flag set. It looks that invalidating status flag here doesn't affect + * further execution. + */ + LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); + MyProc->statusFlags = 0; + ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags; + LWLockRelease(ProcArrayLock); + ColumnarWriteState *writeState = ColumnarBeginWrite(rel->rd_node, columnarOptions, tupleDesc); @@ -1222,8 +1239,8 @@ TruncateAndCombineColumnarStripes(Relation rel, int elevel) randomAccess, NULL); - ColumnaSetStripeReadState(readState, - list_nth(stripeMetadataList, startingStripeListPosition - 1)); + ColumnarSetStripeReadState(readState, + list_nth(stripeMetadataList, startingStripeListPosition - 1)); Datum *values = palloc0(tupleDesc->natts * sizeof(Datum)); bool *nulls = palloc0(tupleDesc->natts * sizeof(bool)); @@ -1249,15 +1266,15 @@ TruncateAndCombineColumnarStripes(Relation rel, int elevel) ColumnarStorageTruncate(rel, newDataReservation); + ColumnarEndWrite(writeState); + ColumnarEndRead(readState); + for (int i = 0; i < startingStripeListPosition; i++) { StripeMetadata *metadata = list_nth(stripeMetadataList, i); DeleteMetadataRowsForStripeId(rel->rd_node, metadata->id); } - ColumnarEndWrite(writeState); - ColumnarEndRead(readState); - return true; } diff --git a/columnar/src/include/columnar/columnar.h b/columnar/src/include/columnar/columnar.h index c77884a3..0406d4c1 100644 --- a/columnar/src/include/columnar/columnar.h +++ b/columnar/src/include/columnar/columnar.h @@ -292,8 +292,8 @@ extern void ColumnarReadRowByRowNumberOrError(ColumnarReadState *readState, extern bool ColumnarReadRowByRowNumber(ColumnarReadState *readState, uint64 rowNumber, Datum *columnValues, bool *columnNulls); -extern bool ColumnaSetStripeReadState(ColumnarReadState *readState, - StripeMetadata *startStripeMetadata); +extern bool ColumnarSetStripeReadState(ColumnarReadState *readState, + StripeMetadata *startStripeMetadata); /* Function declarations for common functions */ extern FmgrInfo * GetFunctionInfoOrNull(Oid typeId, Oid accessMethodId, diff --git a/columnar/src/test/regress/expected/columnar_vacuum.out b/columnar/src/test/regress/expected/columnar_vacuum.out index a537746e..16951b96 100644 --- a/columnar/src/test/regress/expected/columnar_vacuum.out +++ b/columnar/src/test/regress/expected/columnar_vacuum.out @@ -294,9 +294,10 @@ SET columnar.compression TO default; SET columnar.stripe_row_limit TO default; SET columnar.chunk_group_row_limit TO default; CREATE TABLE t(a INT, b INT) USING columnar; +SELECT columnar_test_helpers.columnar_relation_storageid(pg_class.oid) AS t_oid FROM pg_class WHERE relname='t' \gset INSERT INTO t SELECT g, g % 10 from generate_series(1,100000) g; INSERT INTO t SELECT g, g % 10 from generate_series(1,100000) g; -SELECT COUNT(*) = 2 FROM columnar.stripe; +SELECT COUNT(*) = 2 FROM columnar.stripe WHERE storage_id = :t_oid; ?column? ---------- t @@ -305,20 +306,20 @@ SELECT COUNT(*) = 2 FROM columnar.stripe; VACUUM t; -- No change since we can't combine 2 stripe because row_number is higher -- than maximum row number per stripe -SELECT COUNT(*) = 2 FROM columnar.stripe; +SELECT COUNT(*) = 2 FROM columnar.stripe WHERE storage_id = :t_oid; ?column? ---------- t (1 row) DELETE FROM t WHERE a % 2 = 0; -SELECT COUNT(*) = 0 FROM columnar.chunk_group WHERE deleted_rows = 0; +SELECT COUNT(*) = 0 FROM columnar.chunk_group WHERE deleted_rows = 0 AND storage_id = :t_oid; ?column? ---------- t (1 row) -SELECT COUNT(*) = 0 FROM columnar.row_mask WHERE deleted_rows = 0; +SELECT COUNT(*) = 0 FROM columnar.row_mask WHERE deleted_rows = 0 AND storage_id = :t_oid; ?column? ---------- t @@ -327,19 +328,19 @@ SELECT COUNT(*) = 0 FROM columnar.row_mask WHERE deleted_rows = 0; VACUUM t; -- Stripes are merged into one stripe because total number of non-deleted rows -- is less than maximum stripe row number -SELECT COUNT(*) = 1 FROM columnar.stripe; +SELECT COUNT(*) = 1 FROM columnar.stripe WHERE storage_id = :t_oid; ?column? ---------- t (1 row) -SELECT COUNT(*) FROM columnar.chunk_group WHERE deleted_rows = 0; +SELECT COUNT(*) FROM columnar.chunk_group WHERE deleted_rows = 0 AND storage_id = :t_oid; count ------- 10 (1 row) -SELECT COUNT(*) FROM columnar.row_mask WHERE deleted_rows = 0; +SELECT COUNT(*) FROM columnar.row_mask WHERE deleted_rows = 0 AND storage_id = :t_oid; count ------- 10 @@ -348,18 +349,22 @@ SELECT COUNT(*) FROM columnar.row_mask WHERE deleted_rows = 0; DROP TABLE t; -- Vacuum on single stripe CREATE TABLE t(a INT, b INT) USING columnar; +SELECT columnar_test_helpers.columnar_relation_storageid(pg_class.oid) AS t_oid FROM pg_class WHERE relname='t' \gset INSERT INTO t SELECT g, g % 10 from generate_series(1,100000) g; -SELECT COUNT(*) = (SELECT COUNT(*) FROM columnar.row_mask) FROM columnar.chunk_group; +SELECT COUNT(*) = (SELECT COUNT(*) FROM columnar.row_mask WHERE storage_id = :t_oid) + FROM columnar.chunk_group WHERE storage_id = :t_oid; ?column? ---------- t (1 row) -SELECT COUNT(*) AS columnar_chunk_group_rows FROM columnar.chunk_group \gset -SELECT COUNT(*) AS columnar_row_mask_rows FROM columnar.row_mask \gset +SELECT COUNT(*) AS columnar_chunk_group_rows FROM columnar.chunk_group WHERE storage_id = :t_oid \gset +SELECT COUNT(*) AS columnar_row_mask_rows FROM columnar.row_mask WHERE storage_id = :t_oid \gset DELETE FROM t WHERE a % 2 = 0; -SELECT COUNT(*) AS columnar_chunk_group_after_delete_rows FROM columnar.chunk_group WHERE deleted_rows >= 1 \gset -SELECT COUNT(*) AS columnar_row_mask_after_delete_rows FROM columnar.row_mask WHERE deleted_rows >= 1 \gset +SELECT COUNT(*) AS columnar_chunk_group_after_delete_rows FROM columnar.chunk_group + WHERE deleted_rows >= 1 AND storage_id = :t_oid \gset +SELECT COUNT(*) AS columnar_row_mask_after_delete_rows FROM columnar.row_mask + WHERE deleted_rows >= 1 AND storage_id = :t_oid \gset SELECT :columnar_chunk_group_after_delete_rows = :columnar_chunk_group_rows; ?column? ---------- @@ -372,13 +377,13 @@ SELECT :columnar_row_mask_after_delete_rows = :columnar_row_mask_rows; t (1 row) -SELECT SUM(deleted_rows) FROM columnar.row_mask; +SELECT SUM(deleted_rows) FROM columnar.row_mask WHERE storage_id = :t_oid; sum ------- 50000 (1 row) -SELECT SUM(deleted_rows) FROM columnar.chunk_group; +SELECT SUM(deleted_rows) FROM columnar.chunk_group WHERE storage_id = :t_oid; sum ------- 50000 @@ -386,25 +391,27 @@ SELECT SUM(deleted_rows) FROM columnar.chunk_group; VACUUM t; -- No more deleted_rows after vacuum -SELECT COUNT(*) = 0 FROM columnar.chunk_group WHERE deleted_rows >= 1; +SELECT COUNT(*) = 0 FROM columnar.chunk_group WHERE deleted_rows >= 1 AND storage_id = :t_oid; ?column? ---------- t (1 row) -SELECT COUNT(*) = 0 FROM columnar.row_mask WHERE deleted_rows >= 1; +SELECT COUNT(*) = 0 FROM columnar.row_mask WHERE deleted_rows >= 1 AND storage_id = :t_oid; ?column? ---------- t (1 row) -SELECT COUNT(*) = (:columnar_chunk_group_rows / 2) FROM columnar.chunk_group WHERE deleted_rows = 0; +SELECT COUNT(*) = (:columnar_chunk_group_rows / 2) FROM columnar.chunk_group + WHERE deleted_rows = 0 AND storage_id = :t_oid; ?column? ---------- t (1 row) -SELECT COUNT(*) = (:columnar_row_mask_rows / 2) FROM columnar.row_mask WHERE deleted_rows = 0; +SELECT COUNT(*) = (:columnar_row_mask_rows / 2) FROM columnar.row_mask + WHERE deleted_rows = 0 AND storage_id = :t_oid; ?column? ---------- t @@ -421,13 +428,13 @@ SELECT (:table_count_mod_7 / :table_count) < 0.2; DELETE FROM t WHERE a % 7 = 0; VACUUM t; -- Vacuuming will not be done because number of deleted rows / total_rows is less than 20% -SELECT COUNT(*) = (:columnar_chunk_group_rows / 2) FROM columnar.chunk_group; +SELECT COUNT(*) = (:columnar_chunk_group_rows / 2) FROM columnar.chunk_group WHERE storage_id = :t_oid; ?column? ---------- t (1 row) -SELECT COUNT(*) = (:columnar_row_mask_rows / 2) FROM columnar.row_mask ; +SELECT COUNT(*) = (:columnar_row_mask_rows / 2) FROM columnar.row_mask WHERE storage_id = :t_oid; ?column? ---------- t diff --git a/columnar/src/test/regress/sql/columnar_vacuum.sql b/columnar/src/test/regress/sql/columnar_vacuum.sql index 90a4a3b2..c8ea3e83 100644 --- a/columnar/src/test/regress/sql/columnar_vacuum.sql +++ b/columnar/src/test/regress/sql/columnar_vacuum.sql @@ -156,35 +156,36 @@ SET columnar.compression TO default; SET columnar.stripe_row_limit TO default; SET columnar.chunk_group_row_limit TO default; - CREATE TABLE t(a INT, b INT) USING columnar; +SELECT columnar_test_helpers.columnar_relation_storageid(pg_class.oid) AS t_oid FROM pg_class WHERE relname='t' \gset + INSERT INTO t SELECT g, g % 10 from generate_series(1,100000) g; INSERT INTO t SELECT g, g % 10 from generate_series(1,100000) g; -SELECT COUNT(*) = 2 FROM columnar.stripe; +SELECT COUNT(*) = 2 FROM columnar.stripe WHERE storage_id = :t_oid; VACUUM t; -- No change since we can't combine 2 stripe because row_number is higher -- than maximum row number per stripe -SELECT COUNT(*) = 2 FROM columnar.stripe; +SELECT COUNT(*) = 2 FROM columnar.stripe WHERE storage_id = :t_oid; DELETE FROM t WHERE a % 2 = 0; -SELECT COUNT(*) = 0 FROM columnar.chunk_group WHERE deleted_rows = 0; -SELECT COUNT(*) = 0 FROM columnar.row_mask WHERE deleted_rows = 0; +SELECT COUNT(*) = 0 FROM columnar.chunk_group WHERE deleted_rows = 0 AND storage_id = :t_oid; +SELECT COUNT(*) = 0 FROM columnar.row_mask WHERE deleted_rows = 0 AND storage_id = :t_oid; VACUUM t; -- Stripes are merged into one stripe because total number of non-deleted rows -- is less than maximum stripe row number -SELECT COUNT(*) = 1 FROM columnar.stripe; +SELECT COUNT(*) = 1 FROM columnar.stripe WHERE storage_id = :t_oid; -SELECT COUNT(*) FROM columnar.chunk_group WHERE deleted_rows = 0; -SELECT COUNT(*) FROM columnar.row_mask WHERE deleted_rows = 0; +SELECT COUNT(*) FROM columnar.chunk_group WHERE deleted_rows = 0 AND storage_id = :t_oid; +SELECT COUNT(*) FROM columnar.row_mask WHERE deleted_rows = 0 AND storage_id = :t_oid; DROP TABLE t; @@ -192,33 +193,40 @@ DROP TABLE t; CREATE TABLE t(a INT, b INT) USING columnar; +SELECT columnar_test_helpers.columnar_relation_storageid(pg_class.oid) AS t_oid FROM pg_class WHERE relname='t' \gset + INSERT INTO t SELECT g, g % 10 from generate_series(1,100000) g; -SELECT COUNT(*) = (SELECT COUNT(*) FROM columnar.row_mask) FROM columnar.chunk_group; +SELECT COUNT(*) = (SELECT COUNT(*) FROM columnar.row_mask WHERE storage_id = :t_oid) + FROM columnar.chunk_group WHERE storage_id = :t_oid; -SELECT COUNT(*) AS columnar_chunk_group_rows FROM columnar.chunk_group \gset -SELECT COUNT(*) AS columnar_row_mask_rows FROM columnar.row_mask \gset +SELECT COUNT(*) AS columnar_chunk_group_rows FROM columnar.chunk_group WHERE storage_id = :t_oid \gset +SELECT COUNT(*) AS columnar_row_mask_rows FROM columnar.row_mask WHERE storage_id = :t_oid \gset DELETE FROM t WHERE a % 2 = 0; -SELECT COUNT(*) AS columnar_chunk_group_after_delete_rows FROM columnar.chunk_group WHERE deleted_rows >= 1 \gset -SELECT COUNT(*) AS columnar_row_mask_after_delete_rows FROM columnar.row_mask WHERE deleted_rows >= 1 \gset +SELECT COUNT(*) AS columnar_chunk_group_after_delete_rows FROM columnar.chunk_group + WHERE deleted_rows >= 1 AND storage_id = :t_oid \gset +SELECT COUNT(*) AS columnar_row_mask_after_delete_rows FROM columnar.row_mask + WHERE deleted_rows >= 1 AND storage_id = :t_oid \gset SELECT :columnar_chunk_group_after_delete_rows = :columnar_chunk_group_rows; SELECT :columnar_row_mask_after_delete_rows = :columnar_row_mask_rows; -SELECT SUM(deleted_rows) FROM columnar.row_mask; -SELECT SUM(deleted_rows) FROM columnar.chunk_group; +SELECT SUM(deleted_rows) FROM columnar.row_mask WHERE storage_id = :t_oid; +SELECT SUM(deleted_rows) FROM columnar.chunk_group WHERE storage_id = :t_oid; VACUUM t; -- No more deleted_rows after vacuum -SELECT COUNT(*) = 0 FROM columnar.chunk_group WHERE deleted_rows >= 1; -SELECT COUNT(*) = 0 FROM columnar.row_mask WHERE deleted_rows >= 1; +SELECT COUNT(*) = 0 FROM columnar.chunk_group WHERE deleted_rows >= 1 AND storage_id = :t_oid; +SELECT COUNT(*) = 0 FROM columnar.row_mask WHERE deleted_rows >= 1 AND storage_id = :t_oid; -SELECT COUNT(*) = (:columnar_chunk_group_rows / 2) FROM columnar.chunk_group WHERE deleted_rows = 0; -SELECT COUNT(*) = (:columnar_row_mask_rows / 2) FROM columnar.row_mask WHERE deleted_rows = 0; +SELECT COUNT(*) = (:columnar_chunk_group_rows / 2) FROM columnar.chunk_group + WHERE deleted_rows = 0 AND storage_id = :t_oid; +SELECT COUNT(*) = (:columnar_row_mask_rows / 2) FROM columnar.row_mask + WHERE deleted_rows = 0 AND storage_id = :t_oid; SELECT COUNT(*) AS table_count FROM t \gset SELECT COUNT(*) AS table_count_mod_7 FROM t WHERE a % 7 = 0 \gset @@ -231,7 +239,7 @@ VACUUM t; -- Vacuuming will not be done because number of deleted rows / total_rows is less than 20% -SELECT COUNT(*) = (:columnar_chunk_group_rows / 2) FROM columnar.chunk_group; -SELECT COUNT(*) = (:columnar_row_mask_rows / 2) FROM columnar.row_mask ; +SELECT COUNT(*) = (:columnar_chunk_group_rows / 2) FROM columnar.chunk_group WHERE storage_id = :t_oid; +SELECT COUNT(*) = (:columnar_row_mask_rows / 2) FROM columnar.row_mask WHERE storage_id = :t_oid; DROP TABLE t; \ No newline at end of file