Compare commits

...

8 Commits

Author SHA1 Message Date
Andrew Kane
cb246cb72d Improved naming [skip ci] 2026-06-30 02:46:31 -07:00
Andrew Kane
d053de2d94 Removed deletion list check in MarkDeleted (does not help safety) [skip ci] 2026-06-30 02:31:54 -07:00
Andrew Kane
83bac90869 Added checks for deleted tuples rather than relying on ItemPointerIsValid [skip ci] 2026-06-30 01:40:30 -07:00
Andrew Kane
4eca5024df Updated comment [skip ci] 2026-06-30 01:35:07 -07:00
Andrew Kane
a31771bc45 Changed log message to assertion [skip ci] 2026-06-30 01:25:36 -07:00
Andrew Kane
ecddde963a Added check to confirm in deletion list before marking as deleted 2026-06-30 01:15:38 -07:00
Andrew Kane
ecd413d0fe Improved naming [skip ci] 2026-06-30 01:03:08 -07:00
Andrew Kane
497db7976c Fixed hnsw graph not repaired error with HNSW vacuuming - fixes #993 2026-06-30 00:34:01 -07:00
3 changed files with 32 additions and 15 deletions

View File

@@ -1,5 +1,6 @@
## 0.8.4 (unreleased) ## 0.8.4 (unreleased)
- Fixed `hnsw graph not repaired` error with HNSW vacuuming
- Fixed possible error with inserts during HNSW vacuuming - Fixed possible error with inserts during HNSW vacuuming
## 0.8.3 (2026-06-17) ## 0.8.3 (2026-06-17)

View File

@@ -427,7 +427,7 @@ typedef struct HnswVacuumState
HnswSupport support; HnswSupport support;
/* Variables */ /* Variables */
struct tidhash_hash *deleted; struct tidhash_hash *deleting;
BufferAccessStrategy bas; BufferAccessStrategy bas;
HnswNeighborTuple ntup; HnswNeighborTuple ntup;
HnswElementData highestPoint; HnswElementData highestPoint;

View File

@@ -19,12 +19,12 @@
#endif #endif
/* /*
* Check if deleted list contains an index TID * Check if deletion list contains an element
*/ */
static bool static bool
DeletedContains(tidhash_hash * deleted, ItemPointer indextid) DeletingElement(tidhash_hash * deleting, ItemPointer indextid)
{ {
return tidhash_lookup(deleted, *indextid) != NULL; return tidhash_lookup(deleting, *indextid) != NULL;
} }
/* /*
@@ -80,6 +80,14 @@ RemoveHeapTids(HnswVacuumState * vacuumstate)
if (!HnswIsElementTuple(etup)) if (!HnswIsElementTuple(etup))
continue; continue;
/*
* Skip deleted tuples. It is important they are not added to the
* deletion list to avoid false positives in NeedsUpdated and
* ConfirmRepaired.
*/
if (etup->deleted)
continue;
if (ItemPointerIsValid(&etup->heaptids[0])) if (ItemPointerIsValid(&etup->heaptids[0]))
{ {
for (int i = 0; i < HNSW_HEAPTIDS; i++) for (int i = 0; i < HNSW_HEAPTIDS; i++)
@@ -113,13 +121,13 @@ RemoveHeapTids(HnswVacuumState * vacuumstate)
if (!ItemPointerIsValid(&etup->heaptids[0])) if (!ItemPointerIsValid(&etup->heaptids[0]))
{ {
ItemPointerData ip; ItemPointerData indextid;
bool found; bool found;
/* Add to deleted list */ /* Add to deletion list */
ItemPointerSet(&ip, blkno, offno); ItemPointerSet(&indextid, blkno, offno);
tidhash_insert(vacuumstate->deleted, ip, &found); tidhash_insert(vacuumstate->deleting, indextid, &found);
Assert(!found); Assert(!found);
} }
else if (etup->level > highestLevel) else if (etup->level > highestLevel)
@@ -192,8 +200,8 @@ NeedsUpdated(HnswVacuumState * vacuumstate, HnswElement element)
if (!ItemPointerIsValid(indextid)) if (!ItemPointerIsValid(indextid))
continue; continue;
/* Check if in deleted list */ /* Check if in deletion list */
if (DeletedContains(vacuumstate->deleted, indextid)) if (DeletingElement(vacuumstate->deleting, indextid))
{ {
needsUpdated = true; needsUpdated = true;
break; break;
@@ -327,7 +335,7 @@ RepairGraphEntryPoint(HnswVacuumState * vacuumstate)
ItemPointerSet(&epData, entryPoint->blkno, entryPoint->offno); ItemPointerSet(&epData, entryPoint->blkno, entryPoint->offno);
if (DeletedContains(vacuumstate->deleted, &epData)) if (DeletingElement(vacuumstate->deleting, &epData))
{ {
/* /*
* Replace the entry point with the highest point. If highest * Replace the entry point with the highest point. If highest
@@ -413,6 +421,10 @@ RepairGraph(HnswVacuumState * vacuumstate)
if (!HnswIsElementTuple(etup)) if (!HnswIsElementTuple(etup))
continue; continue;
/* Skip deleted tuples */
if (etup->deleted)
continue;
/* Skip updating neighbors if being deleted */ /* Skip updating neighbors if being deleted */
if (!ItemPointerIsValid(&etup->heaptids[0])) if (!ItemPointerIsValid(&etup->heaptids[0]))
continue; continue;
@@ -527,6 +539,10 @@ ConfirmRepaired(HnswVacuumState * vacuumstate)
if (!HnswIsElementTuple(etup)) if (!HnswIsElementTuple(etup))
continue; continue;
/* Skip deleted tuples */
if (etup->deleted)
continue;
/* Skip if being deleted */ /* Skip if being deleted */
if (!ItemPointerIsValid(&etup->heaptids[0])) if (!ItemPointerIsValid(&etup->heaptids[0]))
continue; continue;
@@ -557,8 +573,8 @@ ConfirmRepaired(HnswVacuumState * vacuumstate)
if (!ItemPointerIsValid(indextid)) if (!ItemPointerIsValid(indextid))
continue; continue;
/* Check if in deleted list */ /* Check if in deletion list */
if (DeletedContains(vacuumstate->deleted, indextid)) if (DeletingElement(vacuumstate->deleting, indextid))
elog(ERROR, "hnsw graph not repaired"); elog(ERROR, "hnsw graph not repaired");
} }
@@ -740,7 +756,7 @@ InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkD
HnswGetMetaPageInfo(index, &vacuumstate->m, NULL); HnswGetMetaPageInfo(index, &vacuumstate->m, NULL);
/* Create hash table */ /* Create hash table */
vacuumstate->deleted = tidhash_create(CurrentMemoryContext, 256, NULL); vacuumstate->deleting = tidhash_create(CurrentMemoryContext, 256, NULL);
} }
/* /*
@@ -749,7 +765,7 @@ InitVacuumState(HnswVacuumState * vacuumstate, IndexVacuumInfo *info, IndexBulkD
static void static void
FreeVacuumState(HnswVacuumState * vacuumstate) FreeVacuumState(HnswVacuumState * vacuumstate)
{ {
tidhash_destroy(vacuumstate->deleted); tidhash_destroy(vacuumstate->deleting);
FreeAccessStrategy(vacuumstate->bas); FreeAccessStrategy(vacuumstate->bas);
pfree(vacuumstate->ntup); pfree(vacuumstate->ntup);
MemoryContextDelete(vacuumstate->tmpCtx); MemoryContextDelete(vacuumstate->tmpCtx);