Skip to content

Commit ea22843

Browse files
committed
Add telemetry for access methods
Add telemetry for tracking access methods used, number of pages for each access method, and number of instances using each access method. Also introduces a type-based function `ts_jsonb_set_value_by_type` that can generate correct JSONB based on the PostgreSQL type. It will generate "bare" values for numerics, and strings for anything else using the output function for the type. To test this for string values, we update `ts_jsonb_add_interval` to use this new function, which is calling the output function for the type, just like `ts_jsonb_set_value_by_type`.
1 parent 7ffdd07 commit ea22843

File tree

7 files changed

+167
-14
lines changed

7 files changed

+167
-14
lines changed

.github/workflows/code_style.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666
- name: Run codespell
6767
run: |
6868
find . -type f \( -name "*.c" -or -name "*.h" -or -name "*.yaml" -or -name "*.sh" \) \
69-
-exec codespell -L "inh,larg,inout" {} \+
69+
-exec codespell -L "brin,inh,larg,inout" {} \+
7070
7171
cc_checks:
7272
name: Check code formatting

.unreleased/pr_6810

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implements: #6810 Add telemetry for access methods

src/jsonb_utils.c

+54-12
Original file line numberDiff line numberDiff line change
@@ -57,34 +57,76 @@ ts_jsonb_add_str(JsonbParseState *state, const char *key, const char *value)
5757
ts_jsonb_add_value(state, key, &json_value);
5858
}
5959

60+
static PGFunction
61+
get_convert_func(Oid typeid)
62+
{
63+
switch (typeid)
64+
{
65+
case INT2OID:
66+
return int2_numeric;
67+
case INT4OID:
68+
return int4_numeric;
69+
case INT8OID:
70+
return int8_numeric;
71+
default:
72+
return NULL;
73+
}
74+
}
75+
6076
void
61-
ts_jsonb_add_int32(JsonbParseState *state, const char *key, const int32 int_value)
77+
ts_jsonb_set_value_by_type(JsonbValue *value, Oid typeid, Datum datum)
6278
{
63-
Numeric value;
79+
switch (typeid)
80+
{
81+
Oid typeOut;
82+
bool isvarlena;
83+
char *str;
84+
PGFunction func;
85+
86+
case INT2OID:
87+
case INT4OID:
88+
case INT8OID:
89+
case NUMERICOID:
90+
func = get_convert_func(typeid);
91+
value->type = jbvNumeric;
92+
value->val.numeric = DatumGetNumeric(func ? DirectFunctionCall1(func, datum) : datum);
93+
break;
94+
95+
default:
96+
getTypeOutputInfo(typeid, &typeOut, &isvarlena);
97+
str = OidOutputFunctionCall(typeOut, datum);
98+
value->type = jbvString;
99+
value->val.string.val = str;
100+
value->val.string.len = strlen(str);
101+
break;
102+
}
103+
}
64104

65-
value = DatumGetNumeric(DirectFunctionCall1(int4_numeric, Int32GetDatum(int_value)));
105+
void
106+
ts_jsonb_add_int32(JsonbParseState *state, const char *key, const int32 int_value)
107+
{
108+
JsonbValue json_value;
66109

67-
ts_jsonb_add_numeric(state, key, value);
110+
ts_jsonb_set_value_by_type(&json_value, INT4OID, Int32GetDatum(int_value));
111+
ts_jsonb_add_value(state, key, &json_value);
68112
}
69113

70114
void
71115
ts_jsonb_add_int64(JsonbParseState *state, const char *key, const int64 int_value)
72116
{
73-
Numeric value;
74-
75-
value = DatumGetNumeric(DirectFunctionCall1(int8_numeric, Int64GetDatum(int_value)));
117+
JsonbValue json_value;
76118

77-
ts_jsonb_add_numeric(state, key, value);
119+
ts_jsonb_set_value_by_type(&json_value, INT8OID, Int64GetDatum(int_value));
120+
ts_jsonb_add_value(state, key, &json_value);
78121
}
79122

80123
void
81124
ts_jsonb_add_interval(JsonbParseState *state, const char *key, Interval *interval)
82125
{
83-
char *value;
84-
85-
value = DatumGetCString(DirectFunctionCall1(interval_out, IntervalPGetDatum(interval)));
126+
JsonbValue json_value;
86127

87-
ts_jsonb_add_str(state, key, value);
128+
ts_jsonb_set_value_by_type(&json_value, INTERVALOID, IntervalPGetDatum(interval));
129+
ts_jsonb_add_value(state, key, &json_value);
88130
}
89131

90132
void

src/jsonb_utils.h

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ extern TSDLLEXPORT void ts_jsonb_add_int64(JsonbParseState *state, const char *k
2323
const int64 value);
2424
extern TSDLLEXPORT void ts_jsonb_add_numeric(JsonbParseState *state, const char *key,
2525
const Numeric value);
26+
extern TSDLLEXPORT void ts_jsonb_set_value_by_type(JsonbValue *value, Oid typeid, Datum datum);
2627

2728
extern void ts_jsonb_add_value(JsonbParseState *state, const char *key, JsonbValue *value);
2829

src/telemetry/telemetry.c

+90
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,87 @@ add_replication_telemetry(JsonbParseState *state)
757757
#define REQ_RELS_CONTINUOUS_AGGS "continuous_aggregates"
758758
#define REQ_FUNCTIONS_USED "functions_used"
759759
#define REQ_REPLICATION "replication"
760+
#define REQ_ACCESS_METHODS "access_methods"
761+
762+
/*
763+
* Add the result of a query as a sub-object to the JSONB.
764+
*
765+
* Each row from the query generates a separate object keyed by one of the
766+
* columns. Each row will be represented as an object and stored under the
767+
* "key" column. For example, with this query:
768+
*
769+
* select amname as name,
770+
* sum(relpages) as pages,
771+
* count(*) as instances
772+
* from pg_class join pg_am on relam = pg_am.oid
773+
* group by pg_am.oid;
774+
*
775+
* might generate the object
776+
*
777+
* {
778+
* "brin" : {
779+
* "instances" : 44,
780+
* "pages" : 432
781+
* },
782+
* "btree" : {
783+
* "instances" : 99,
784+
* "pages" : 1234
785+
* }
786+
* }
787+
*/
788+
static void
789+
add_query_result_dict(JsonbParseState *state, const char *query)
790+
{
791+
MemoryContext orig_context = CurrentMemoryContext;
792+
793+
int res;
794+
if (SPI_connect() != SPI_OK_CONNECT)
795+
elog(ERROR, "could not connect to SPI");
796+
797+
/* Lock down search_path */
798+
res = SPI_execute("SET LOCAL search_path TO pg_catalog, pg_temp", false, 0);
799+
Ensure(res >= 0, "could not set search path");
800+
801+
res = SPI_execute(query, true, 0);
802+
Ensure(res >= 0, "could not execute query");
803+
804+
MemoryContext spi_context = MemoryContextSwitchTo(orig_context);
805+
806+
(void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
807+
for (uint64 r = 0; r < SPI_processed; r++)
808+
{
809+
char *key_string = SPI_getvalue(SPI_tuptable->vals[r], SPI_tuptable->tupdesc, 1);
810+
JsonbValue key = {
811+
.type = jbvString,
812+
.val.string.val = pstrdup(key_string),
813+
.val.string.len = strlen(key_string),
814+
};
815+
816+
(void) pushJsonbValue(&state, WJB_KEY, &key);
817+
818+
(void) pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
819+
for (int c = 1; c < SPI_tuptable->tupdesc->natts; ++c)
820+
{
821+
bool isnull;
822+
Datum val_datum =
823+
SPI_getbinval(SPI_tuptable->vals[r], SPI_tuptable->tupdesc, c + 1, &isnull);
824+
if (!isnull)
825+
{
826+
char *key_string = SPI_fname(SPI_tuptable->tupdesc, c + 1);
827+
JsonbValue value;
828+
ts_jsonb_set_value_by_type(&value,
829+
SPI_gettypeid(SPI_tuptable->tupdesc, c + 1),
830+
val_datum);
831+
ts_jsonb_add_value(state, key_string, &value);
832+
}
833+
}
834+
pushJsonbValue(&state, WJB_END_OBJECT, NULL);
835+
}
836+
MemoryContextSwitchTo(spi_context);
837+
res = SPI_finish();
838+
Assert(res == SPI_OK_FINISH);
839+
(void) pushJsonbValue(&state, WJB_END_OBJECT, NULL);
840+
}
760841

761842
static Jsonb *
762843
build_telemetry_report()
@@ -937,6 +1018,15 @@ build_telemetry_report()
9371018
add_replication_telemetry(parse_state);
9381019
pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL);
9391020

1021+
key.type = jbvString;
1022+
key.val.string.val = REQ_ACCESS_METHODS;
1023+
key.val.string.len = strlen(REQ_ACCESS_METHODS);
1024+
(void) pushJsonbValue(&parse_state, WJB_KEY, &key);
1025+
add_query_result_dict(parse_state,
1026+
"SELECT amname AS name, sum(relpages) AS pages, count(*) AS "
1027+
"instances FROM pg_class JOIN pg_am ON relam = pg_am.oid "
1028+
"GROUP BY amname");
1029+
9401030
/* end of telemetry object */
9411031
result = pushJsonbValue(&parse_state, WJB_END_OBJECT, NULL);
9421032

test/expected/telemetry.out

+13-1
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ WHERE key != 'os_name_pretty';
350350
db_metadata
351351
replication
352352
build_os_name
353+
access_methods
353354
functions_used
354355
install_method
355356
installed_time
@@ -377,7 +378,7 @@ WHERE key != 'os_name_pretty';
377378
num_compression_policies_fixed
378379
num_user_defined_actions_fixed
379380
num_continuous_aggs_policies_fixed
380-
(37 rows)
381+
(38 rows)
381382

382383
CREATE MATERIALIZED VIEW telemetry_report AS
383384
SELECT t FROM get_telemetry_report() t;
@@ -395,6 +396,17 @@ SELECT t -> 'instance_metadata' FROM telemetry_report;
395396
{"cloud": "ci"}
396397
(1 row)
397398

399+
-- Check access methods
400+
SELECT t->'access_methods' ? 'btree',
401+
t->'access_methods' ? 'heap',
402+
CAST(t->'access_methods'->'btree'->'pages' AS int) > 0,
403+
CAST(t->'access_methods'->'btree'->'instances' AS int) > 0
404+
FROM telemetry_report;
405+
?column? | ?column? | ?column? | ?column?
406+
----------+----------+----------+----------
407+
t | t | t | t
408+
(1 row)
409+
398410
WITH t AS (
399411
SELECT t -> 'relations' AS rels
400412
FROM telemetry_report

test/sql/telemetry.sql

+7
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ SELECT t -> 'db_metadata' FROM telemetry_report;
166166
-- check timescaledb_telemetry.cloud
167167
SELECT t -> 'instance_metadata' FROM telemetry_report;
168168

169+
-- Check access methods
170+
SELECT t->'access_methods' ? 'btree',
171+
t->'access_methods' ? 'heap',
172+
CAST(t->'access_methods'->'btree'->'pages' AS int) > 0,
173+
CAST(t->'access_methods'->'btree'->'instances' AS int) > 0
174+
FROM telemetry_report;
175+
169176
WITH t AS (
170177
SELECT t -> 'relations' AS rels
171178
FROM telemetry_report

0 commit comments

Comments
 (0)