From: Robert Haas Date: Thu, 3 Jul 2025 16:05:49 +0000 (-0400) Subject: add ability to store in the shared collector X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=c7fdb269796ef8bf974ede27d4188c95d568f8a6;p=users%2Frhaas%2Fpostgres.git add ability to store in the shared collector --- diff --git a/contrib/pg_plan_advice/pg_plan_advice.c b/contrib/pg_plan_advice/pg_plan_advice.c index d002e9d26f..27f81729f1 100644 --- a/contrib/pg_plan_advice/pg_plan_advice.c +++ b/contrib/pg_plan_advice/pg_plan_advice.c @@ -85,8 +85,7 @@ pgpa_init_shared_state(void *ptr) LWLockInitialize(&state->lock, LWLockNewTrancheId()); state->dsa_tranche = LWLockNewTrancheId(); state->area = DSA_HANDLE_INVALID; - state->advice_count = 0; - state->advice_array = InvalidDsaPointer; + state->shared_collector = InvalidDsaPointer; } /* @@ -131,6 +130,9 @@ pg_plan_advice_attach(void) return pgpa_state; } +/* + * Return a pointer to pg_plan_advice's DSA area, creating it if needed. + */ dsa_area * pg_plan_advice_dsa_area(void) { diff --git a/contrib/pg_plan_advice/pg_plan_advice.h b/contrib/pg_plan_advice/pg_plan_advice.h index 796921b067..cb4592a76a 100644 --- a/contrib/pg_plan_advice/pg_plan_advice.h +++ b/contrib/pg_plan_advice/pg_plan_advice.h @@ -20,8 +20,7 @@ typedef struct pgpa_shared_state LWLock lock; int dsa_tranche; dsa_handle area; - uint64 advice_count; - dsa_pointer advice_array; + dsa_pointer shared_collector; } pgpa_shared_state; /* GUC variables */ diff --git a/contrib/pg_plan_advice/pgpa_collector.c b/contrib/pg_plan_advice/pgpa_collector.c index 9cad190337..d4b6e00a2f 100644 --- a/contrib/pg_plan_advice/pgpa_collector.c +++ b/contrib/pg_plan_advice/pgpa_collector.c @@ -29,6 +29,10 @@ PG_FUNCTION_INFO_V1(pg_get_collected_local_advice); #define PG_GET_ADVICE_COLUMNS 7 +/* + * Advice extracted from one query plan, together with the query string + * and various other identifying details. + */ typedef struct pgpa_collected_advice { Oid userid; /* user OID */ @@ -39,11 +43,23 @@ typedef struct pgpa_collected_advice char textual_data[FLEXIBLE_ARRAY_MEMBER]; } pgpa_collected_advice; +/* + * A bunch of pointers to pgpa_collected_advice objects, stored in + * backend-local memory. + */ typedef struct pgpa_local_advice_chunk { pgpa_collected_advice *entries[ADVICE_CHUNK_SIZE]; } pgpa_local_advice_chunk; +/* + * Information about all of the pgpa_collected_advice objects that we're + * storing in local memory. + * + * We assign consecutive IDs, starting from 0, to each pgpa_collected_advice + * object that we store. The actual storage is an array of chunks, which + * helps keep memcpy() overhead low when we start discarding older data. + */ typedef struct pgpa_local_advice { uint64 next_id; @@ -53,8 +69,33 @@ typedef struct pgpa_local_advice pgpa_local_advice_chunk **chunks; } pgpa_local_advice; -static pgpa_local_advice *local_advice; +/* + * Just like pgpa_local_advice_chunk, but stored in a dynamic shared area, + * so we must use dsa_pointer instead of native pointers. + */ +typedef struct pgpa_shared_advice_chunk +{ + dsa_pointer entries[ADVICE_CHUNK_SIZE]; +} pgpa_shared_advice_chunk; + +/* + * Just like pgpa_local_advice, but stored in a dynamic shared area, so + * we must use dsa_pointer instead of native pointers. + */ +typedef struct pgpa_shared_advice +{ + uint64 next_id; + uint64 oldest_id; + uint64 base_id; + int chunk_array_allocated_size; + dsa_pointer chunks; +} pgpa_shared_advice; + +/* Pointers to local and shared collectors */ +static pgpa_local_advice *local_collector = NULL; +static pgpa_shared_advice *shared_collector = NULL; +/* Static functions */ static pgpa_collected_advice *pgpa_make_collected_advice(Oid userid, Oid dbid, uint64 queryId, @@ -65,6 +106,7 @@ static pgpa_collected_advice *pgpa_make_collected_advice(Oid userid, dsa_pointer *result); static void pgpa_store_local_advice(pgpa_collected_advice *ca); static void pgpa_trim_local_advice(void); +static void pgpa_store_shared_advice(dsa_pointer ca_pointer); /* Helper function to extract the query string from pgpa_collected_advice */ static inline const char * @@ -114,7 +156,7 @@ pgpa_collect_advice(uint64 queryId, const char *query_string, pgpa_make_collected_advice(userid, dbid, queryId, now, query_string, advice_string, area, &ca_pointer); - /* XXX do something */ + pgpa_store_shared_advice(ca_pointer); /* XXX over limit? */ } } @@ -175,7 +217,7 @@ pgpa_store_local_advice(pgpa_collected_advice *ca) { uint64 chunk_number; uint64 chunk_offset; - pgpa_local_advice *la = local_advice; + pgpa_local_advice *la = local_collector; /* If the local advice collector isn't initialized yet, do that now. */ if (la == NULL) @@ -184,7 +226,7 @@ pgpa_store_local_advice(pgpa_collected_advice *ca) la->chunk_array_allocated_size = ADVICE_CHUNK_ARRAY_SIZE; la->chunks = palloc0_array(pgpa_local_advice_chunk *, la->chunk_array_allocated_size); - local_advice = la; + local_collector = la; } /* Compute chunk and offset at which to store this advice. */ @@ -220,7 +262,7 @@ pgpa_store_local_advice(pgpa_collected_advice *ca) static void pgpa_trim_local_advice(void) { - pgpa_local_advice *la = local_advice; + pgpa_local_advice *la = local_collector; uint64 current_count; uint64 trim_count; uint64 total_chunk_count; @@ -266,6 +308,94 @@ pgpa_trim_local_advice(void) la->base_id += trim_chunk_count * ADVICE_CHUNK_SIZE; } +/* + * Add a pg_collected_advice object to the shared advice collection. + * + * 'ca_pointer' should have been allocated from the pg_plan_advice DSA area + * and should point to an object of type pgpa_collected_advice. + */ +static void +pgpa_store_shared_advice(dsa_pointer ca_pointer) +{ + uint64 chunk_number; + uint64 chunk_offset; + pgpa_shared_state *state = pg_plan_advice_attach(); + dsa_area *area = pg_plan_advice_dsa_area(); + pgpa_shared_advice *sa = shared_collector; + dsa_pointer *chunk_array; + pgpa_shared_advice_chunk *chunk; + + /* Lock the shared state. */ + LWLockAcquire(&state->lock, LW_EXCLUSIVE); + + /* + * If we're not attached to the shared advice collector yet, fix that now. + * If we're the first ones to attach, we may need to create the object. + */ + if (sa == NULL) + { + if (state->shared_collector == InvalidDsaPointer) + state->shared_collector = + dsa_allocate0(area, sizeof(pgpa_shared_advice)); + shared_collector = sa = dsa_get_address(area, state->shared_collector); + } + + /* + * It's possible that some other backend may have succeeded in creating + * the main collector object but failed to allocate an initial chunk array, + * so we must be prepared to allocate the chunk array here whether or not + * we created the collector object. + */ + if (shared_collector->chunk_array_allocated_size == 0) + { + sa->chunks = + dsa_allocate0(area, + sizeof(dsa_pointer) * ADVICE_CHUNK_ARRAY_SIZE); + sa->chunk_array_allocated_size = ADVICE_CHUNK_ARRAY_SIZE; + } + + /* Compute chunk and offset at which to store this advice. */ + chunk_number = (sa->next_id - sa->base_id) / ADVICE_CHUNK_SIZE; + chunk_offset = (sa->next_id - sa->base_id) % ADVICE_CHUNK_SIZE; + + /* Get the address of the chunk array and, if needed, extend it. */ + if (chunk_number > sa->chunk_array_allocated_size) + { + int new_size; + dsa_pointer new_chunks; + + /* + * DSA can't enlarge an existing allocation, so we must make a new + * allocation and copy data over. + */ + new_size = sa->chunk_array_allocated_size + ADVICE_CHUNK_ARRAY_SIZE; + new_chunks = dsa_allocate0(area, sizeof(dsa_pointer) * new_size); + chunk_array = dsa_get_address(area, new_chunks); + memcpy(chunk_array, dsa_get_address(area, sa->chunks), + sizeof(dsa_pointer) * sa->chunk_array_allocated_size); + dsa_free(area, sa->chunks); + sa->chunks = new_chunks; + sa->chunk_array_allocated_size = new_size; + } + else + chunk_array = dsa_get_address(area, sa->chunks); + + /* Get the address of the desired chunk, allocating it if needed. */ + if (chunk_array[chunk_number] == InvalidDsaPointer) + chunk_array[chunk_number] = + dsa_allocate0(area, sizeof(pgpa_shared_advice_chunk)); + chunk = dsa_get_address(area, chunk_array[chunk_number]); + + /* Save pointer and bump next-id counter. */ + Assert(chunk->entries[chunk_offset] == InvalidDsaPointer); + chunk->entries[chunk_offset] = ca_pointer; + ++sa->next_id; + + /* Release lock on shared state. */ + LWLockRelease(&state->lock); + +} + /* * SQL-callable SRF to return locally collected advice */ @@ -273,7 +403,7 @@ Datum pg_get_collected_local_advice(PG_FUNCTION_ARGS) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; - pgpa_local_advice *la = local_advice; + pgpa_local_advice *la = local_collector; /* * XXX. Is this the correct thing from a security point of view?