First revision

This commit is contained in:
James Campbell 2025-04-04 17:11:35 -04:00
parent 232614a894
commit 834e646b0e
Signed by: james
GPG Key ID: 2287C33A40DC906A
2 changed files with 213 additions and 27 deletions

View File

@ -1,8 +1,23 @@
CREATE FUNCTION pg_lock_hash_info() CREATE FUNCTION pg_lock_hash_info()
RETURNS TABLE ( RETURNS TABLE (
hash_type CHAR, dsize INT,
nsegs INT,
max_bucket INT,
keysize INT,
entrysize INT,
num_partitions INT,
max_dsize INT,
ssize INT,
sshift INT,
nelem_alloc INT
) AS 'pg_lock_hash_info', 'pg_lock_hash_info'
LANGUAGE C STRICT;
CREATE FUNCTION pg_lock_hash_bucket_info()
RETURNS TABLE (
directory INT, directory INT,
bucket INT,
bucket_hash INT, bucket_hash INT,
entries INT, entries INT
free_slots INT ) AS 'pg_lock_hash_info', 'pg_lock_hash_bucket_info'
) AS 'pg_lock_hash_info' LANGUAGE C STRICT; LANGUAGE C STRICT;

View File

@ -1,3 +1,5 @@
/* pg_lock_hash_info.c - PostgreSQL extension to expose lock and proclock hash bucket statistics */
#include "postgres.h" #include "postgres.h"
#include "fmgr.h" #include "fmgr.h"
#include "access/htup_details.h" #include "access/htup_details.h"
@ -5,21 +7,165 @@
#include "storage/lwlock.h" #include "storage/lwlock.h"
#include "storage/shmem.h" #include "storage/shmem.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/hsearch.h"
#include "utils/dynahash.h"
PG_MODULE_MAGIC; PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(pg_lock_hash_info); HTAB *lock_hash = NULL;
typedef struct { // BEGIN Copied from dynahash.c
int bucket_idx;
char hash_type; #define DEF_SEGSIZE 256
int directory; #define DEF_SEGSIZE_SHIFT 8 /* must be log2(DEF_SEGSIZE) */
int entries; #define DEF_DIRSIZE 256
int free_slots; #define NUM_FREELISTS 32
} LockHashEntry;
typedef HASHELEMENT *HASHBUCKET;
typedef HASHBUCKET *HASHSEGMENT;
typedef struct
{
slock_t mutex; /* spinlock for this freelist */
long nentries; /* number of entries in associated buckets */
HASHELEMENT *freeList; /* chain of free elements */
} FreeListData;
struct HASHHDR
{
FreeListData freeList[NUM_FREELISTS];
long dsize; /* directory size */
long nsegs; /* number of allocated segments (<= dsize) */
uint32 max_bucket; /* ID of maximum bucket in use */
uint32 high_mask; /* mask to modulo into entire table */
uint32 low_mask; /* mask to modulo into lower half of table */
Size keysize; /* hash key length in bytes */
Size entrysize; /* total user element size in bytes */
long num_partitions; /* # partitions (must be power of 2), or 0 */
long max_dsize; /* 'dsize' limit if directory is fixed size */
long ssize; /* segment size --- must be power of 2 */
int sshift; /* segment shift = log2(ssize) */
int nelem_alloc; /* number of entries to allocate at once */
#ifdef HASH_STATISTICS
long accesses;
long collisions;
#endif
};
struct HTAB
{
HASHHDR *hctl; /* => shared control information */
HASHSEGMENT *dir; /* directory of segment starts */
HashValueFunc hash; /* hash function */
HashCompareFunc match; /* key comparison function */
HashCopyFunc keycopy; /* key copying function */
HashAllocFunc alloc; /* memory allocator */
MemoryContext hcxt; /* memory context if default allocator used */
char *tabname; /* table name (for error messages) */
bool isshared; /* true if table is in shared memory */
bool isfixed; /* if true, don't enlarge */
bool frozen; /* true = no more inserts allowed */
Size keysize; /* hash key length in bytes */
long ssize; /* segment size --- must be power of 2 */
int sshift; /* segment shift = log2(ssize) */
};
// END Copied from dynahash.c
void get_lock_hash();
void
get_lock_hash()
{
HASHCTL info;
lock_hash = ShmemInitHash("LOCK hash", 0, 0, &info, HASH_ATTACH);
if (!lock_hash)
{
elog(ERROR, "Failed to find lock hash");
}
}
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;
Datum values[10];
bool nulls[10];
HeapTuple tuple;
// Get a pointer to the lock hash
get_lock_hash();
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context that cannot accept type record")));
values[0] = Int32GetDatum(lock_hash->hctl->dsize);
nulls[0] = false;
values[1] = Int32GetDatum(lock_hash->hctl->nsegs);
nulls[1] = false;
values[2] = UInt32GetDatum(lock_hash->hctl->max_bucket);
nulls[2] = false;
values[3] = UInt32GetDatum(lock_hash->hctl->keysize);
nulls[3] = false;
values[4] = UInt32GetDatum(lock_hash->hctl->entrysize);
nulls[4] = false;
values[5] = Int32GetDatum(lock_hash->hctl->num_partitions);
nulls[5] = false;
values[6] = Int32GetDatum(lock_hash->hctl->max_dsize);
nulls[6] = false;
values[7] = Int32GetDatum(lock_hash->hctl->ssize);
nulls[7] = false;
values[8] = Int32GetDatum(lock_hash->hctl->sshift);
nulls[8] = false;
values[9] = Int32GetDatum(lock_hash->hctl->nelem_alloc);
nulls[9] = false;
tuple = heap_form_tuple(tupdesc, values, nulls);
PG_RETURN_DATUM(HeapTupleGetDatum(tuple));
}
int count_list(HASHELEMENT *root);
int
count_list(HASHELEMENT *root)
{
int count = 0;
HASHELEMENT *elem = root;
while (elem != NULL) {
count++;
elem = elem->link;
}
return count;
}
typedef struct {
int directory;
int bucket;
int bucket_hash;
int entries;
} LockHashEntry;
PG_FUNCTION_INFO_V1(pg_lock_hash_bucket_info);
Datum
pg_lock_hash_bucket_info(PG_FUNCTION_ARGS)
{ {
FuncCallContext *funcctx; FuncCallContext *funcctx;
LockHashEntry *entries; LockHashEntry *entries;
@ -28,39 +174,64 @@ pg_lock_hash_info(PG_FUNCTION_ARGS)
{ {
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(5); tupdesc = CreateTemplateTupleDesc(4);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "hash_type", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "directory", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "directory", 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, "entries", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "free_slots", INT4OID, -1, 0);
funcctx->tuple_desc = BlessTupleDesc(tupdesc); funcctx->tuple_desc = BlessTupleDesc(tupdesc);
/* Allocate space for results (this should fetch real data) */ /* Allocate and initialize the entries */
entries = palloc(sizeof(LockHashEntry) * 10); /* Placeholder for real count */ entries = (LockHashEntry *) palloc(sizeof(LockHashEntry) * lock_hash->hctl->nsegs * lock_hash->hctl->max_bucket);
for (i = 0; i < lock_hash->hctl->nsegs; i++)
{
for (j = 0; j < lock_hash->hctl->max_bucket; j++)
{
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->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; entries = (LockHashEntry *) funcctx->user_fctx;
/* Placeholder loop to return mock data */ if (funcctx->call_cntr < funcctx->max_calls)
if (funcctx->call_cntr < 10)
{ {
Datum values[5]; Datum values[4];
bool nulls[5] = {false, false, false, false, false}; bool nulls[4];
HeapTuple tuple; HeapTuple tuple;
LockHashEntry *entry = &entries[funcctx->call_cntr];
values[0] = CharGetDatum(funcctx->call_cntr % 2 == 0 ? 'l' : 'p'); values[0] = Int32GetDatum(entry->directory);
values[1] = Int32GetDatum(funcctx->call_cntr / 5); nulls[0] = false;
values[2] = Int32GetDatum(funcctx->call_cntr);
values[3] = Int32GetDatum(5); /* Fake number of entries */ values[1] = Int32GetDatum(entry->bucket);
values[4] = Int32GetDatum(10); /* Fake number of free slots */ 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); tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));