Skip to content

Commit c3d29d0

Browse files
perf: Optimize DELETE/DETACH DELETE performance
- Remove unnecessary sequential scans in DETACH DELETE. - ~30x performance improvement from some rough tests. - Added caches and indexes to find connected edges efficiently. - Setting auto_gather_graphmeta to true automatically triggers regather_graphmeta to ensure correct metadata. - When auto_gather_graphmeta is enabled, DETACH DELETE prunes the ag_edge inheritance tree and scans only edge labels connected to the target vertex. This optimization currently applies to vertices only, not paths. This further optimizes the DELETE operation. Future Considerations: - Maybe we should remove the edge count column in ag_graphmeta which could slow down writes a bit and enable graphmeta permanently? - If no edge is connected, remove the join completely.
1 parent 98c3cc3 commit c3d29d0

File tree

21 files changed

+697
-296
lines changed

21 files changed

+697
-296
lines changed

src/backend/catalog/ag_graph.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@
2121
#include "catalog/pg_namespace.h"
2222
#include "commands/schemacmds.h"
2323
#include "utils/builtins.h"
24+
#include "utils/graph.h"
2425
#include "utils/guc_hooks.h"
2526
#include "utils/lsyscache.h"
2627
#include "utils/rel.h"
2728
#include "utils/syscache.h"
2829
#include "catalog/catalog.h"
2930
#include "miscadmin.h"
31+
#include "pgstat.h"
3032

3133
/* a global variable for the GUC variable */
3234
char *graph_path = NULL;
@@ -36,6 +38,28 @@ bool cypher_allow_unsafe_ddl = false;
3638
/* Potentially set by pg_upgrade_support functions */
3739
Oid binary_upgrade_next_ag_graph_oid = InvalidOid;
3840

41+
/* assign_hook for auto_gather_graphmeta */
42+
static bool prev_auto_gather_graphmeta = false;
43+
void auto_gather_graphmeta_assign(bool newval, void *extra)
44+
{
45+
// Only trigger on false->true transition
46+
if (newval && !prev_auto_gather_graphmeta && !IsParallelWorker())
47+
{
48+
if (IsTransactionState())
49+
{
50+
regather_graphmeta_internal();
51+
}
52+
else
53+
{
54+
ereport(WARNING,
55+
(errmsg("auto_gather_graphmeta: cannot gather metadata outside transaction"),
56+
errhint("Metadata will be gathered when set within a transaction.")));
57+
}
58+
}
59+
60+
prev_auto_gather_graphmeta = newval;
61+
}
62+
3963
/* check_hook: validate new graph_path value */
4064
bool
4165
check_graph_path(char **newval, void **extra, GucSource source)

src/backend/catalog/ag_label.c

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
#include "access/heapam.h"
1515
#include "access/htup_details.h"
1616
#include "catalog/ag_graph_fn.h"
17+
#include "catalog/ag_label_fn.h"
18+
#include "catalog/ag_graphmeta.h"
1719
#include "catalog/ag_label.h"
1820
#include "catalog/ag_label_fn.h"
1921
#include "catalog/catalog.h"
2022
#include "catalog/indexing.h"
2123
#include "commands/sequence.h"
2224
#include "utils/builtins.h"
25+
#include "utils/catcache.h"
2326
#include "utils/graph.h"
2427
#include "utils/rel.h"
2528
#include "utils/lsyscache.h"
@@ -164,48 +167,50 @@ GetNewLabelId(char *graphname, Oid graphid)
164167
}
165168

166169
/*
167-
* Retrieves a list of all the names of a graph.
170+
* Retrieves a list of edge label OIDs that are connected to a specific vertex label.
171+
* Finds edges where the given vertex label appears as either start or end vertex.
168172
*/
169173
List *
170-
get_all_edge_labels_per_graph(Snapshot snapshot, Oid graph_oid)
174+
get_connected_edge_labels_for_vertex(Snapshot snapshot, Oid graph_oid, Labid vertex_labid)
171175
{
172-
List *labels = NIL;
173-
ScanKeyData scan_keys[2];
174-
Relation ag_label;
175-
TableScanDesc scan_desc;
176-
HeapTuple tuple;
177-
178-
ScanKeyInit(&scan_keys[0],
179-
Anum_ag_label_graphid,
180-
BTEqualStrategyNumber,
181-
F_OIDEQ,
182-
ObjectIdGetDatum(graph_oid));
183-
ScanKeyInit(&scan_keys[1],
184-
Anum_ag_label_labkind,
185-
BTEqualStrategyNumber,
186-
F_CHAREQ,
187-
CharGetDatum(LABEL_KIND_EDGE));
188-
189-
ag_label = table_open(LabelRelationId, RowExclusiveLock);
190-
scan_desc = table_beginscan(ag_label, snapshot, 2, scan_keys);
191-
192-
while ((tuple = heap_getnext(scan_desc, ForwardScanDirection)) != NULL)
193-
{
194-
Oid label_rel_oid;
195-
bool isnull;
196-
Datum datum;
197-
198-
datum = heap_getattr(tuple,
199-
Anum_ag_label_relid,
200-
RelationGetDescr(ag_label),
201-
&isnull);
202-
label_rel_oid = DatumGetObjectId(datum);
176+
List *edge_labels = NIL;
177+
List *seen_edge_labels = NIL;
178+
int i;
179+
CatCList *tuplist = SearchSysCacheList2(GRAPHMETASTART, graph_oid, vertex_labid);
203180

204-
labels = lappend_oid(labels, label_rel_oid);
181+
for (i = 0; i < tuplist->n_members; i++)
182+
{
183+
HeapTuple tup = &tuplist->members[i]->tuple;
184+
Form_ag_graphmeta metatup = (Form_ag_graphmeta) GETSTRUCT(tup);
185+
Labid edge_labid = metatup->edge;
186+
187+
/* Avoid duplicates */
188+
if (!list_member_int(seen_edge_labels, edge_labid))
189+
{
190+
Oid edge_relid = get_labid_relid(graph_oid, edge_labid);
191+
edge_labels = lappend_oid(edge_labels, edge_relid);
192+
seen_edge_labels = lappend_int(seen_edge_labels, edge_labid);
193+
}
205194
}
195+
ReleaseCatCacheList(tuplist);
206196

207-
table_endscan(scan_desc);
208-
table_close(ag_label, RowExclusiveLock);
197+
tuplist = SearchSysCacheList2(GRAPHMETAEND, graph_oid, vertex_labid);
198+
for (i = 0; i < tuplist->n_members; i++)
199+
{
200+
HeapTuple tup = &tuplist->members[i]->tuple;
201+
Form_ag_graphmeta metatup = (Form_ag_graphmeta) GETSTRUCT(tup);
202+
Labid edge_labid = metatup->edge;
203+
204+
/* Avoid duplicates */
205+
if (!list_member_int(seen_edge_labels, edge_labid))
206+
{
207+
Oid edge_relid = get_labid_relid(graph_oid, edge_labid);
208+
edge_labels = lappend_oid(edge_labels, edge_relid);
209+
seen_edge_labels = lappend_int(seen_edge_labels, edge_labid);
210+
}
211+
}
212+
ReleaseCatCacheList(tuplist);
209213

210-
return labels;
214+
list_free(seen_edge_labels);
215+
return edge_labels;
211216
}

0 commit comments

Comments
 (0)