Add some views and more data

This commit is contained in:
James Campbell 2025-04-17 19:45:53 -04:00
parent 834e646b0e
commit 34cf4770c0
Signed by: james
GPG Key ID: 2287C33A40DC906A
2 changed files with 267 additions and 66 deletions

View File

@ -9,15 +9,57 @@ RETURNS TABLE (
max_dsize INT, max_dsize INT,
ssize INT, ssize INT,
sshift INT, sshift INT,
nelem_alloc INT nelem_alloc INT,
nfree INT,
nlockents INT,
low_mask INT,
high_mask INT
) AS 'pg_lock_hash_info', 'pg_lock_hash_info' ) AS 'pg_lock_hash_info', 'pg_lock_hash_info'
LANGUAGE C STRICT; LANGUAGE C STRICT;
CREATE FUNCTION pg_lock_hash_freelist_info()
RETURNS TABLE (
freelist INT,
nentries INT,
count INT
) AS 'pg_lock_hash_info', 'pg_lock_hash_freelist_info'
LANGUAGE C STRICT;
CREATE FUNCTION pg_lock_hash_bucket_info() CREATE FUNCTION pg_lock_hash_bucket_info()
RETURNS TABLE ( RETURNS TABLE (
directory INT, segment INT,
bucket INT, bucket INT,
bucket_hash INT, bucket_hash INT,
bucket_hash_low INT,
bucket_hash_high INT,
entries INT entries INT
) AS 'pg_lock_hash_info', 'pg_lock_hash_bucket_info' ) AS 'pg_lock_hash_info', 'pg_lock_hash_bucket_info'
LANGUAGE C STRICT; LANGUAGE C STRICT;
CREATE VIEW pg_lock_hash_info AS
SELECT * from pg_lock_hash_info();
CREATE VIEW pg_lock_hash_freelist_info AS
SELECT * FROM pg_lock_hash_freelist_info();
CREATE VIEW pg_lock_hash_bucket_info AS
SELECT * from pg_lock_hash_bucket_info();
CREATE VIEW pg_lock_hash_freelist_summary AS
SELECT count(*) AS freelist_total,
sum(nentries) AS nentries_total,
avg(nentries) AS nentries_avg,
stddev_pop(nentries) nentries_stddev,
sum(count) AS count_total,
avg(count) AS count_avg,
stddev_pop(count) AS count_stddev,
sum(nentries+count) AS total
FROM pg_lock_hash_freelist_info();
CREATE VIEW pg_lock_hash_bucket_summary AS
SELECT count(*) AS buckets_total,
sum(entries) AS total_active_locks,
avg(entries) AS avg_bucket_size,
stddev_pop(entries) AS stddev_bucket_size
FROM pg_lock_hash_bucket_info();

View File

@ -3,9 +3,13 @@
#include "postgres.h" #include "postgres.h"
#include "fmgr.h" #include "fmgr.h"
#include "access/htup_details.h" #include "access/htup_details.h"
#include "miscadmin.h" // Access to MaxBackends and MaxConnections
#include "access/twophase.h" // Access to max_prepared_xacts
#include "funcapi.h" #include "funcapi.h"
#include "storage/lock.h"
#include "storage/lwlock.h" #include "storage/lwlock.h"
#include "storage/shmem.h" #include "storage/shmem.h"
#include "storage/spin.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/hsearch.h" #include "utils/hsearch.h"
#include "utils/dynahash.h" #include "utils/dynahash.h"
@ -14,8 +18,13 @@ PG_MODULE_MAGIC;
HTAB *lock_hash = NULL; HTAB *lock_hash = NULL;
void get_lock_hash();
// BEGIN Copied from dynahash.c // BEGIN Copied from dynahash.c
#define NLOCKENTS() \
mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
#define DEF_SEGSIZE 256 #define DEF_SEGSIZE 256
#define DEF_SEGSIZE_SHIFT 8 /* must be log2(DEF_SEGSIZE) */ #define DEF_SEGSIZE_SHIFT 8 /* must be log2(DEF_SEGSIZE) */
#define DEF_DIRSIZE 256 #define DEF_DIRSIZE 256
@ -72,27 +81,69 @@ struct HTAB
// END Copied from dynahash.c // END Copied from dynahash.c
void get_lock_hash();
void void
get_lock_hash() get_lock_hash()
{ {
HASHCTL info; HASHCTL info;
lock_hash = ShmemInitHash("LOCK hash", 0, 0, &info, HASH_ATTACH); long init_table_size, max_table_size;
bool found;
max_table_size = NLOCKENTS();
init_table_size = max_table_size / 2;
info.keysize = sizeof(LOCKTAG);
info.entrysize = sizeof(LOCK);
info.num_partitions = NUM_LOCK_PARTITIONS;
lock_hash = ShmemInitHash("LOCK hash", init_table_size, max_table_size, &info, HASH_ELEM | HASH_BLOBS | HASH_PARTITION);
if (!lock_hash) if (!lock_hash)
{ {
elog(ERROR, "Failed to find lock hash"); elog(ERROR, "Failed to find lock hash");
} }
} }
int
get_free_count(FreeListData *lst)
{
HASHELEMENT *elem;
int count = 0;
SpinLockAcquire(&(lst->mutex));
elem = lst->freeList;
while (elem != NULL)
{
count++;
elem = elem->link;
}
SpinLockRelease(&(lst->mutex));
return count;
}
int
get_total_free_count(HASHHDR *hctl)
{
int i;
int count = 0;
for (i=0; i<NUM_FREELISTS; i++)
{
count += get_free_count(&(hctl->freeList[i]));
}
return count;
}
PG_FUNCTION_INFO_V1(pg_lock_hash_info); PG_FUNCTION_INFO_V1(pg_lock_hash_info);
Datum Datum
pg_lock_hash_info(PG_FUNCTION_ARGS) pg_lock_hash_info(PG_FUNCTION_ARGS)
{ {
TupleDesc tupdesc; TupleDesc tupdesc;
Datum values[10]; Datum values[14];
bool nulls[10]; bool nulls[14];
HeapTuple tuple; HeapTuple tuple;
// Get a pointer to the lock hash // Get a pointer to the lock hash
@ -104,9 +155,11 @@ pg_lock_hash_info(PG_FUNCTION_ARGS)
errmsg("function returning record called in context that cannot accept type record"))); errmsg("function returning record called in context that cannot accept type record")));
values[0] = Int32GetDatum(lock_hash->hctl->dsize); values[0] = Int32GetDatum(lock_hash->hctl->dsize);
//values[0] = Int32GetDatum(MaxBackends);
nulls[0] = false; nulls[0] = false;
values[1] = Int32GetDatum(lock_hash->hctl->nsegs); values[1] = Int32GetDatum(lock_hash->hctl->nsegs);
//values[1] = Int32GetDatum(MaxConnections);
nulls[1] = false; nulls[1] = false;
values[2] = UInt32GetDatum(lock_hash->hctl->max_bucket); values[2] = UInt32GetDatum(lock_hash->hctl->max_bucket);
@ -133,19 +186,102 @@ pg_lock_hash_info(PG_FUNCTION_ARGS)
values[9] = Int32GetDatum(lock_hash->hctl->nelem_alloc); values[9] = Int32GetDatum(lock_hash->hctl->nelem_alloc);
nulls[9] = false; nulls[9] = false;
values[10] = Int32GetDatum(get_total_free_count(lock_hash->hctl));
nulls[10] = false;
values[11] = Int32GetDatum(NLOCKENTS());
nulls[11] = false;
values[12] = Int32GetDatum(lock_hash->hctl->low_mask);
nulls[12] = false;
values[13] = Int32GetDatum(lock_hash->hctl->high_mask);
nulls[13] = false;
tuple = heap_form_tuple(tupdesc, values, nulls); tuple = heap_form_tuple(tupdesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
} }
int count_list(HASHELEMENT *root); PG_FUNCTION_INFO_V1(pg_lock_hash_freelist_info);
Datum
pg_lock_hash_freelist_info(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
int *list_id;
if (SRF_IS_FIRSTCALL())
{
TupleDesc tupdesc;
MemoryContext oldcontext;
funcctx = SRF_FIRSTCALL_INIT();
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
// Get a pointer to the lock hash
get_lock_hash();
tupdesc = CreateTemplateTupleDesc(3);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "freelist", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "nentries", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "count", INT4OID, -1, 0);
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
/* Allocate and initialize the position */
funcctx->user_fctx = palloc(sizeof(int));
list_id = (int *) funcctx->user_fctx;
*list_id = 0;
MemoryContextSwitchTo(oldcontext);
}
funcctx = SRF_PERCALL_SETUP();
list_id = (int *) funcctx->user_fctx;
if (*list_id >= NUM_FREELISTS)
{
SRF_RETURN_DONE(funcctx);
}
else
{
Datum values[4];
bool nulls[4];
HeapTuple tuple;
FreeListData *freelist = &(lock_hash->hctl->freeList[*list_id]);
values[0] = Int32GetDatum(*list_id);
nulls[0] = false;
values[1] = Int32GetDatum(freelist->nentries);
nulls[1] = false;
values[2] = Int32GetDatum(get_free_count(freelist));
nulls[2] = false;
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
// Increment the position, handling segment wraparound
(*list_id)++;
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
}
}
int int
count_list(HASHELEMENT *root) count_list(HASHBUCKET root)
{ {
int count = 0; int count = 0;
HASHELEMENT *elem = root; HASHELEMENT *elem;
if (!root)
{
return 0;
}
elem = root->link;
while (elem != NULL) { while (elem != NULL) {
count++; count++;
@ -155,90 +291,113 @@ count_list(HASHELEMENT *root)
return count; return count;
} }
typedef struct {
int directory;
int bucket;
int bucket_hash;
int entries;
} LockHashEntry;
PG_FUNCTION_INFO_V1(pg_lock_hash_bucket_info); PG_FUNCTION_INFO_V1(pg_lock_hash_bucket_info);
typedef struct {
int segment_id;
int bucket_id;
} LockHashBucketInfoPosition;
Datum Datum
pg_lock_hash_bucket_info(PG_FUNCTION_ARGS) pg_lock_hash_bucket_info(PG_FUNCTION_ARGS)
{ {
FuncCallContext *funcctx; FuncCallContext *funcctx;
LockHashEntry *entries; LockHashBucketInfoPosition *pos;
LWLock *part_lock;
if (SRF_IS_FIRSTCALL()) if (SRF_IS_FIRSTCALL())
{ {
TupleDesc tupdesc; TupleDesc tupdesc;
MemoryContext oldcontext; MemoryContext oldcontext;
int i, j;
//HASHSEGMENT segp;
HASHBUCKET bucketp;
// Get a pointer to the lock hash
get_lock_hash();
funcctx = SRF_FIRSTCALL_INIT(); funcctx = SRF_FIRSTCALL_INIT();
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
tupdesc = CreateTemplateTupleDesc(4); // Get a pointer to the lock hash
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "directory", INT4OID, -1, 0); get_lock_hash();
tupdesc = CreateTemplateTupleDesc(6);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "segment", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "bucket", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "bucket", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "bucket_hash", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "bucket_hash", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "entries", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 4, "bucket_hash_low", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "bucket_hash_high", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "entries", INT4OID, -1, 0);
funcctx->tuple_desc = BlessTupleDesc(tupdesc); funcctx->tuple_desc = BlessTupleDesc(tupdesc);
/* Allocate and initialize the entries */ /* Allocate and initialize the position */
entries = (LockHashEntry *) palloc(sizeof(LockHashEntry) * lock_hash->hctl->nsegs * lock_hash->hctl->max_bucket); funcctx->user_fctx = palloc(sizeof(LockHashBucketInfoPosition));
for (i = 0; i < lock_hash->hctl->nsegs; i++) pos = (LockHashBucketInfoPosition *) funcctx->user_fctx;
{ pos->segment_id = 0;
for (j = 0; j < lock_hash->hctl->max_bucket; j++) pos->bucket_id = 0;
{
bucketp = lock_hash->dir[i][j];
entries[i].directory = i;
entries[i].bucket = j;
entries[i].bucket_hash = bucketp ? bucketp->hashvalue : 'na';
entries[i].entries = count_list(bucketp);
}
}
funcctx->user_fctx = entries;
funcctx->max_calls = lock_hash->hctl->nsegs * 10; // * lock_hash->hctl->max_bucket;
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
} }
funcctx = SRF_PERCALL_SETUP(); funcctx = SRF_PERCALL_SETUP();
entries = (LockHashEntry *) funcctx->user_fctx; pos = (LockHashBucketInfoPosition *) funcctx->user_fctx;
if (funcctx->call_cntr < funcctx->max_calls) if (pos->segment_id >= lock_hash->hctl->nsegs)
{
Datum values[4];
bool nulls[4];
HeapTuple tuple;
LockHashEntry *entry = &entries[funcctx->call_cntr];
values[0] = Int32GetDatum(entry->directory);
nulls[0] = false;
values[1] = Int32GetDatum(entry->bucket);
nulls[1] = false;
values[2] = Int32GetDatum(entry->bucket_hash);
nulls[2] = false;
values[3] = Int32GetDatum(entry->entries);
nulls[3] = false;
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
}
else
{ {
SRF_RETURN_DONE(funcctx); SRF_RETURN_DONE(funcctx);
} }
else
{
Datum values[6];
bool nulls[6];
HeapTuple tuple;
HASHBUCKET bucketp = lock_hash->dir[pos->segment_id][pos->bucket_id];
values[0] = Int32GetDatum(pos->segment_id);
nulls[0] = false;
values[1] = Int32GetDatum(pos->bucket_id);
nulls[1] = false;
if (bucketp)
{
values[2] = Int32GetDatum(bucketp->hashvalue);
nulls[2] = false;
values[3] = Int32GetDatum(bucketp->hashvalue & lock_hash->hctl->low_mask);
nulls[3] = false;
values[4] = Int32GetDatum(bucketp->hashvalue & lock_hash->hctl->high_mask);
nulls[4] = false;
part_lock = LockHashPartitionLock(bucketp->hashvalue);
LWLockAcquire(part_lock, LW_SHARED);
values[5] = UInt32GetDatum(count_list(bucketp));
nulls[5] = false;
LWLockRelease(part_lock);
}
else
{
values[2] = Int32GetDatum(0);
nulls[2] = true;
values[3] = Int32GetDatum(0);
nulls[3] = true;
values[4] = Int32GetDatum(0);
nulls[4] = true;
values[5] = Int32GetDatum(0);
nulls[5] = false;
}
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
// Increment the position, handling segment wraparound
pos->bucket_id++;
while (pos->bucket_id >= lock_hash->hctl->ssize)
{
pos->bucket_id -= lock_hash->hctl->ssize;
pos->segment_id++;
}
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
}
} }