v00 just the counter

Cette révision appartient à :
Cédric Villemain 2024-10-20 14:51:05 +02:00
révision 0b9b6aa124
9 fichiers modifiés avec 629 ajouts et 0 suppressions

21
Makefile Fichier normal
Voir le fichier

@ -0,0 +1,21 @@
EXTENSION = pacs
DATA = pacs--0.0.1.sql
REGRESS = pacs
MODULES = pacs
TAP_TESTS = 1
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
%.man.1: README.%.md
pandoc -s $< -o $@
@echo man -l $@
pgbench: NPROCS=$(shell nproc)
pgbench: NJOBS=$(shell nproc)
pgbench: sql/pgbench.sql
pgbench -f $< -T 10 --progress 1 --no-vacuum \
-c $$(( $(NPROCS) * 4 )) \
-j $$(( $(NJOBS) * 4 ))

0
README.pacs.md Fichier normal
Voir le fichier

152
expected/pacs.out Fichier normal
Voir le fichier

@ -0,0 +1,152 @@
CREATE EXTENSION slim;
CREATE EXTENSION pacs;
SELECT pacs_reset_all_measures();
pacs_reset_all_measures
-------------------------
(1 row)
-- create a measure
SELECT pacs_create_measure('A');
pacs_create_measure
---------------------
(1 row)
-- increment it 3 times
SELECT pacs_increment_measure('A');
pacs_increment_measure
------------------------
(1 row)
SELECT pacs_increment_measure('A');
pacs_increment_measure
------------------------
(1 row)
SELECT pacs_increment_measure('A');
pacs_increment_measure
------------------------
(1 row)
-- check measure value is 3
SELECT pacs_get_measure('A');
pacs_get_measure
------------------
3
(1 row)
-- reset the measure
SELECT pacs_reset_measure('A');
pacs_reset_measure
--------------------
(1 row)
-- should be 0 now
SELECT pacs_get_measure('A');
pacs_get_measure
------------------
0
(1 row)
-- increment again
SELECT pacs_increment_measure('A');
pacs_increment_measure
------------------------
(1 row)
-- should be 1
SELECT pacs_get_measure('A');
pacs_get_measure
------------------
1
(1 row)
-- and drop the measure
SELECT pacs_drop_measure('A');
pacs_drop_measure
-------------------
(1 row)
-- get stats from pacs (empty)
-- SELECT * FROM pacs_get_stats();
-- create 2 measures
SELECT pacs_create_measure('B');
pacs_create_measure
---------------------
(1 row)
SELECT pacs_create_measure('C');
pacs_create_measure
---------------------
(1 row)
-- expect 2 measures
-- SELECT * FROM pacs_get_stats();
-- drop one
SELECT pacs_drop_measure('B');
pacs_drop_measure
-------------------
(1 row)
-- so result now is 1
-- SELECT * FROM pacs_get_stats();
--
-- SPECIAL CASES
--
-- measure A has been dropped already.
-- but it's not erroing, as expected.
SELECT pacs_drop_measure('A');
pacs_drop_measure
-------------------
(1 row)
-- trying to get value for an inexisting measure
-- should ERROR
SELECT pacs_get_measure('absent');
ERROR: measure "absent" does not exist
HINT: ensure the measure name is correct or $$ select create_measure('absent')$$ to create it
-- reseting an inexsting measure
-- but it's not erroing, as expected
SELECT pacs_reset_measure('absent');
pacs_reset_measure
--------------------
(1 row)
-- the measure still does not exist
-- so we have an error
SELECT pacs_get_measure('absent');
ERROR: measure "absent" does not exist
HINT: ensure the measure name is correct or $$ select create_measure('absent')$$ to create it
-- however increment always create the measure if it does not exists
SELECT pacs_increment_measure('absent');
pacs_increment_measure
------------------------
(1 row)
-- and now we can fetch value (should be 1)
SELECT pacs_get_measure('absent');
pacs_get_measure
------------------
1
(1 row)
-- and we drop the "absent" measure (yeah, it's confusing)
SELECT pacs_drop_measure('absent');
pacs_drop_measure
-------------------
(1 row)

30
pacs--0.0.1.sql Fichier normal
Voir le fichier

@ -0,0 +1,30 @@
/*
* measures
*/
CREATE FUNCTION pacs_reset_all_measures()
RETURNS void
AS 'MODULE_PATHNAME', 'pacs_reset_all_measures'
LANGUAGE C STRICT;
CREATE FUNCTION pacs_reset_measure(measure_name Name)
RETURNS void
AS 'MODULE_PATHNAME', 'pacs_reset_measure'
LANGUAGE C STRICT;
CREATE FUNCTION pacs_create_measure(measure_name Name)
RETURNS void
AS 'MODULE_PATHNAME', 'pacs_create_measure'
LANGUAGE C STRICT;
CREATE FUNCTION pacs_drop_measure(measure_name Name)
RETURNS void
AS 'MODULE_PATHNAME', 'pacs_drop_measure'
LANGUAGE C STRICT;
CREATE FUNCTION pacs_increment_measure(measure_name Name)
RETURNS void
AS 'MODULE_PATHNAME', 'pacs_increment_measure'
LANGUAGE C STRICT;
CREATE FUNCTION pacs_get_measure(measure_name Name)
RETURNS bigint
AS 'MODULE_PATHNAME', 'pacs_get_measure'
LANGUAGE C STRICT;

333
pacs.c Fichier normal
Voir le fichier

@ -0,0 +1,333 @@
/*--------------------------------------------------------------------------
*
* pacs.c
* Code for easier management of "measures".
*
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "fmgr.h"
#include "common/hashfn.h"
#include "pgstat.h"
#include "utils/builtins.h"
#include "utils/pgstat_internal.h"
#include "pacs.h"
PG_MODULE_MAGIC;
/* structure for measure */
typedef struct MeasureStatEntry
{
PgStat_Counter measure;
} MeasureStatEntry;
/* boilerplace structure for all measures */
typedef struct MeasuresStatShared
{
PgStatShared_Common header;
MeasureStatEntry stats;
} MeasuresStatShared;
/* boilerplate declaration */
static bool measure_flush_cb(PgStat_EntryRef *entry_ref, bool nowait);
/* declare our measures stats */
static const PgStat_KindInfo measures = {
.name = "measures",
.fixed_amount = false,
/* Measures are system-wide */
.accessed_across_databases = true,
.shared_size = sizeof(MeasuresStatShared),
.shared_data_off = offsetof(MeasuresStatShared, stats),
.shared_data_len = sizeof(((MeasuresStatShared *) 0)->stats),
.pending_size = sizeof(MeasureStatEntry),
.flush_pending_cb = measure_flush_cb,
};
/*
* Compute stats entry idx from measure name with an 8-byte hash.
*/
#define PGSTAT_MEASURE_IDX(name) hash_bytes_extended((const unsigned char *) name, strlen(name), 0)
/*
* Kind ID reserved for statistics of measures.
*/
#define PGSTAT_KIND_MEASURE (PGSTAT_KIND_CUSTOM_MIN + 1)
/* Track if stats are loaded */
static bool measures_loaded = false;
void
_PG_init(void)
{
if (!process_shared_preload_libraries_in_progress)
return;
ereport(LOG,
(errmsg("Module pacs is starting"),
errdetail("providing advanced stats structures.")));
pgstat_register_kind(PGSTAT_KIND_MEASURE, &measures);
/* mark stats as loaded */
measures_loaded = true;
}
/*
* Callback for flushing a measure
*/
static bool
measure_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
{
MeasureStatEntry *localent;
MeasuresStatShared *shfuncent;
localent = (MeasureStatEntry *) entry_ref->pending;
shfuncent = (MeasuresStatShared *) entry_ref->shared_stats;
if (!pgstat_lock_entry(entry_ref, nowait))
return false;
shfuncent->stats.measure += localent->measure;
return true;
}
static void
check_loaded()
{
if (!measures_loaded)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("measures module not loaded"),
errhint("check shared_preload_libraries")));
}
/*
* The following code contains:
* - the exported C functions
* - the SQL API
*/
/*
* Create a measure
*/
PGDLLEXPORT void
pacsCreateMeasure(const char *name)
{
PgStat_EntryRef *entry_ref;
MeasuresStatShared *shstatent;
/* error if disabled */
check_loaded();
entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_MEASURE, InvalidOid,
PGSTAT_MEASURE_IDX(name), false);
shstatent = (MeasuresStatShared *) entry_ref->shared_stats;
/* initialize shared memory data */
memset(&shstatent->stats, 0, sizeof(shstatent->stats));
pgstat_unlock_entry(entry_ref);
}
/*
* SQL API
*/
PG_FUNCTION_INFO_V1(pacs_create_measure);
Datum
pacs_create_measure(PG_FUNCTION_ARGS)
{
Name name;
name = PG_GETARG_NAME(0);
pacsCreateMeasure(NameStr(*name));
PG_RETURN_VOID();
}
/*
* Drop a measure
*
* XXX register to create, drop to unregister...
*/
PGDLLEXPORT void
pacsDropMeasure(const char *name)
{
/* error if disabled */
check_loaded();
/* drop and on faillure inform for later garbage collection */
if (!pgstat_drop_entry(PGSTAT_KIND_MEASURE, InvalidOid,
PGSTAT_MEASURE_IDX(name)))
pgstat_request_entry_refs_gc();
}
PG_FUNCTION_INFO_V1(pacs_drop_measure);
Datum
pacs_drop_measure(PG_FUNCTION_ARGS)
{
Name name;
name = PG_GETARG_NAME(0);
pacsDropMeasure(NameStr(*name));
PG_RETURN_VOID();
}
/*
* Increment a measure
*
* By design, PostgreSQL does create the measure if absent!
*
* we do not manage ereport() here to not add overhead.
* XXX is it accurate ?
*/
PGDLLEXPORT void
pacsIncrementMeasure(const char *name)
{
PgStat_EntryRef *entry_ref;
MeasuresStatShared *shstatent;
MeasureStatEntry *statent;
/* quick leave if disabled */
if (!measures_loaded)
return;
entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_MEASURE, InvalidOid,
PGSTAT_MEASURE_IDX(name), false);
shstatent = (MeasuresStatShared *) entry_ref->shared_stats;
statent = &shstatent->stats;
/* Update the measure */
statent->measure++;
pgstat_unlock_entry(entry_ref);
}
PG_FUNCTION_INFO_V1(pacs_increment_measure);
Datum
pacs_increment_measure(PG_FUNCTION_ARGS)
{
Name name;
name = PG_GETARG_NAME(0);
pacsIncrementMeasure(NameStr(*name));
PG_RETURN_VOID();
}
/*
* Support function for the SQL-callable functions. Returns
* a pointer to the measure struct.
*/
static MeasureStatEntry *
pacsGetMeasure(const char *name)
{
MeasureStatEntry *entry = NULL;
/* error if disabled */
check_loaded();
/* Compile the lookup key as a hash of the measure name */
entry = (MeasureStatEntry *) pgstat_fetch_entry(PGSTAT_KIND_MEASURE,
InvalidOid,
PGSTAT_MEASURE_IDX(name));
return entry;
}
/*
* SQL function returning the measure current value.
*/
PG_FUNCTION_INFO_V1(pacs_get_measure);
Datum
pacs_get_measure(PG_FUNCTION_ARGS)
{
MeasureStatEntry *entry;
Name name;
name = PG_GETARG_NAME(0);
entry = pacsGetMeasure(NameStr(*name));
if (entry == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("measure \"%s\" does not exist",
NameStr(*name)),
errhint("ensure the measure name is correct or "
"$$ select create_measure('%s')$$ to create it",
NameStr(*name))));
PG_RETURN_INT64(entry->measure);
}
/*
* Reset a measure
*/
PGDLLEXPORT void
pacsResetMeasure(const char *name)
{
/* error if disabled */
check_loaded();
pgstat_reset_entry(PGSTAT_KIND_MEASURE, InvalidOid,
PGSTAT_MEASURE_IDX(name), 0);
return;
}
/*
* SQL function reseting the measure(s)
*/
PG_FUNCTION_INFO_V1(pacs_reset_measure);
Datum
pacs_reset_measure(PG_FUNCTION_ARGS)
{
Name name;
name = PG_GETARG_NAME(0);
pacsResetMeasure(NameStr(*name));
PG_RETURN_VOID();
}
/*
* Reset a measure
*/
PGDLLEXPORT void
pacsResetAllMeasures(void)
{
/* error if disabled */
check_loaded();
pgstat_reset_of_kind(PGSTAT_KIND_MEASURE);
return;
}
PG_FUNCTION_INFO_V1(pacs_reset_all_measures);
Datum
pacs_reset_all_measures(PG_FUNCTION_ARGS)
{
pacsResetAllMeasures();
PG_RETURN_VOID();
}
// PG_FUNCTION_INFO_V1(pacs_list_all_measures);
// PGDLLEXPORT void
// pacs_list_all_measures(PgStat_Kind kind)
// {
// HASH_SEQ_STATUS status;
// PgStat_EntryRef *entry_ref;
//
// const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
// if (!kind_info || !kind_info->hash_table)
// {
// elog(ERROR, "Invalid or unsupported statistics kind.");
// return;
// }
//
// hash_seq_init(&status, kind_info->hash_table);
//
// while ((entry_ref = hash_seq_search(&status)) != NULL)
// {
// elog(INFO, "Key for kind %d: %u/%llu", kind,
// entry_ref->key.dboid, (unsigned long long)entry_ref->key.objid);
// }
// }

5
pacs.control Fichier normal
Voir le fichier

@ -0,0 +1,5 @@
comment = 'Provides PostgresSQL Advanced Cumulative Statistics'
default_version = '0.0.1'
relocatable = true
module_pathname = '$libdir/pacs'
requires = 'slim'

29
pacs.h Fichier normal
Voir le fichier

@ -0,0 +1,29 @@
/*--------------------------------------------------------------------------
*
* pacs.h
* Definitions for easier statistics management.
*
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* -------------------------------------------------------------------------
*/
#ifndef PACS
#define PACS
extern void RegisterMeasuresInPgStat(void);
/* creating/dropping stats */
extern void pacsCreateMeasure(const char *name);
extern void pacsDropMeasure(const char *name);
/* working with stats content */
extern void pacsIncrementMeasure(const char *name);
extern void pacsResetAllMeasures(void);
extern void pacsResetMeasure(const char *name);
/* statsmgr_stats.c */
extern void RegisterPacsInPgStat(void);
#endif

59
sql/pacs.sql Fichier normal
Voir le fichier

@ -0,0 +1,59 @@
CREATE EXTENSION slim;
CREATE EXTENSION pacs;
SELECT pacs_reset_all_measures();
-- create a measure
SELECT pacs_create_measure('A');
-- increment it 3 times
SELECT pacs_increment_measure('A');
SELECT pacs_increment_measure('A');
SELECT pacs_increment_measure('A');
-- check measure value is 3
SELECT pacs_get_measure('A');
-- reset the measure
SELECT pacs_reset_measure('A');
-- should be 0 now
SELECT pacs_get_measure('A');
-- increment again
SELECT pacs_increment_measure('A');
-- should be 1
SELECT pacs_get_measure('A');
-- and drop the measure
SELECT pacs_drop_measure('A');
-- get stats from pacs (empty)
-- SELECT * FROM pacs_get_stats();
-- create 2 measures
SELECT pacs_create_measure('B');
SELECT pacs_create_measure('C');
-- expect 2 measures
-- SELECT * FROM pacs_get_stats();
-- drop one
SELECT pacs_drop_measure('B');
-- so result now is 1
-- SELECT * FROM pacs_get_stats();
--
-- SPECIAL CASES
--
-- measure A has been dropped already.
-- but it's not erroing, as expected.
SELECT pacs_drop_measure('A');
-- trying to get value for an inexisting measure
-- should ERROR
SELECT pacs_get_measure('absent');
-- reseting an inexsting measure
-- but it's not erroing, as expected
SELECT pacs_reset_measure('absent');
-- the measure still does not exist
-- so we have an error
SELECT pacs_get_measure('absent');
-- however increment always create the measure if it does not exists
SELECT pacs_increment_measure('absent');
-- and now we can fetch value (should be 1)
SELECT pacs_get_measure('absent');
-- and we drop the "absent" measure (yeah, it's confusing)
SELECT pacs_drop_measure('absent');

0
sql/pgbench.sql Fichier normal
Voir le fichier