jrgemignani commented on code in PR #2376:
URL: https://github.com/apache/age/pull/2376#discussion_r3060952430
##########
src/backend/commands/label_commands.c:
##########
@@ -432,6 +434,64 @@ static void create_table_for_label(char *graph_name, char
*label_name,
create_index_on_column(schema_name, rel_name, "start_id", false);
create_index_on_column(schema_name, rel_name, "end_id", false);
}
+
+ /*
+ * Install a cache invalidation trigger on the new label table, if the
+ * trigger function exists. The function is registered in the extension
+ * SQL (age_main.sql). It may not exist if running against an older
+ * version of the extension SQL that hasn't been upgraded yet.
+ *
+ * When installed, the trigger fires AFTER INSERT/UPDATE/DELETE/TRUNCATE
+ * (FOR EACH STATEMENT) and increments the graph's version counter so
+ * VLE caches are properly invalidated when the table is modified via SQL.
+ */
+ {
+ Oid func_oid;
+
+ /* check if the trigger function is registered in the catalog */
+ func_oid = LookupFuncName(
+ list_make2(makeString("ag_catalog"),
+ makeString("age_invalidate_graph_cache")),
+ 0, NULL, true);
+
+ if (OidIsValid(func_oid))
+ {
+ CreateTrigStmt *trigger_stmt = makeNode(CreateTrigStmt);
+ PlannedStmt *trigger_wrapper;
+
+ trigger_stmt->replace = false;
+ trigger_stmt->isconstraint = false;
+ trigger_stmt->trigname = "_age_cache_invalidate";
+ trigger_stmt->relation = makeRangeVar(graph_name, rel_name, -1);
Review Comment:
Resolved
##########
src/backend/utils/adt/age_global_graph.c:
##########
@@ -1450,3 +1595,272 @@ Datum age_graph_stats(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(agtype_value_to_agtype(result.res));
}
+
+/*
+ * ============================================================================
+ * Graph Version Counter Implementation
+ *
+ * Provides per-graph monotonic version counters in shared memory for
+ * cross-backend VLE cache invalidation. Three modes are supported:
+ *
+ * DSM (PG 17+): Uses GetNamedDSMSegment — works without shared_preload_libs
+ * SHMEM (PG <17): Uses shmem_request/startup hooks — needs shared_preload_libs
+ * SNAPSHOT: Falls back to original snapshot-based invalidation
+ * ============================================================================
+ */
+
+#if PG_VERSION_NUM >= 170000
+/*
+ * DSM path: GetNamedDSMSegment init callback.
+ * Called once when the DSM segment is first created.
+ */
+static void age_dsm_init_callback(void *ptr)
+{
+ GraphVersionState *state = (GraphVersionState *) ptr;
+
+ LWLockInitialize(&state->lock,
+ LWLockNewTrancheId());
+ LWLockRegisterTranche(state->lock.tranche, "age_graph_version");
+ state->num_entries = 0;
+ memset(state->entries, 0, sizeof(state->entries));
+}
+
+/*
+ * Get the shared GraphVersionState via DSM registry.
+ * The segment is created on first access and persists until server shutdown.
+ */
+static GraphVersionState *get_version_state_dsm(void)
+{
+ bool found;
+
+ return (GraphVersionState *)
+ GetNamedDSMSegment("age_graph_versions",
+ sizeof(GraphVersionState),
+ age_dsm_init_callback,
+ &found);
+}
+#endif /* PG_VERSION_NUM >= 170000 */
+
+/*
+ * SHMEM path: request and startup hooks for PG < 17.
+ * These are registered in _PG_init when shared_preload_libraries is used.
+ */
+void age_graph_version_shmem_request(void)
+{
+ RequestAddinShmemSpace(MAXALIGN(sizeof(GraphVersionState)));
+ RequestNamedLWLockTranche("age_graph_version", 1);
+}
+
+void age_graph_version_shmem_startup(void)
+{
+ bool found;
+
+ LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
+
+ shmem_version_state =
+ (GraphVersionState *) ShmemInitStruct("AGE Graph Version State",
+ sizeof(GraphVersionState),
+ &found);
+ if (!found)
+ {
+ LWLockInitialize(&shmem_version_state->lock,
+ LWLockNewTrancheId());
+ LWLockRegisterTranche(shmem_version_state->lock.tranche,
+ "age_graph_version");
+ shmem_version_state->num_entries = 0;
+ memset(shmem_version_state->entries, 0,
+ sizeof(shmem_version_state->entries));
+ }
+
+ LWLockRelease(AddinShmemInitLock);
+}
+
+/*
+ * Detect which version mode to use. Called once per backend on first access.
+ * Emits a DEBUG1 log message indicating the chosen mode.
+ */
+static void detect_version_mode(void)
+{
+#if PG_VERSION_NUM >= 170000
+ version_mode = VERSION_MODE_DSM;
+ elog(DEBUG1, "AGE: VLE cache using DSM version counter");
+#else
+ if (shmem_version_state != NULL)
+ {
+ version_mode = VERSION_MODE_SHMEM;
+ elog(DEBUG1, "AGE: VLE cache using SHMEM version counter");
+ }
+ else
+ {
+ version_mode = VERSION_MODE_SNAPSHOT;
+ elog(DEBUG1, "AGE: VLE cache using snapshot-based invalidation "
+ "(add AGE to shared_preload_libraries for better caching)");
+ }
+#endif
+}
+
+/*
+ * Get a pointer to the GraphVersionState, regardless of mode.
+ * Returns NULL only in SNAPSHOT mode (no shared memory available).
+ */
+static GraphVersionState *get_version_state(void)
+{
+ if (version_mode == VERSION_MODE_UNKNOWN)
+ detect_version_mode();
+
+#if PG_VERSION_NUM >= 170000
+ if (version_mode == VERSION_MODE_DSM)
+ return get_version_state_dsm();
+#endif
+
+ if (version_mode == VERSION_MODE_SHMEM)
+ return shmem_version_state;
+
+ return NULL;
+}
+
+/*
+ * Get the current version counter for a graph.
+ * Returns 0 if the graph has never been tracked or if shared memory
+ * is not available. Lock-free read via pg_atomic_read_u64.
+ */
+uint64 get_graph_version(Oid graph_oid)
+{
+ GraphVersionState *state = get_version_state();
+ int i;
+
+ if (state == NULL)
+ return 0;
+
+ /* lock-free scan of the array */
+ for (i = 0; i < state->num_entries; i++)
+ {
+ if (state->entries[i].graph_oid == graph_oid)
+ return pg_atomic_read_u64(&state->entries[i].version);
+ }
Review Comment:
Resolved
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]