First revision
This commit is contained in:
parent
232614a894
commit
834e646b0e
@ -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;
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user