Skip to content

Commit 38e7e40

Browse files
committed
feat: add pgwatch sequence_overflow metric for L003 report
Add sequence_overflow metric to pgwatch configuration that monitors sequence-generated columns (serial/identity) for potential integer overflow risk. The metric: - Queries pg_depend, pg_class, pg_attribute, pg_sequence to find all sequence-column relationships - Calculates percentage used for both sequence and column data types - Excludes system schemas (pg_catalog, information_schema, pg_toast) - Sorts by highest risk (greatest percentage used) - Limits to 1000 results per database SQL query based on approach from Laurenz Albe at CYBERTEC: https://www.cybertec-postgresql.com/en/integer-overflow-in-sequence-generated-primary-keys/ Collection interval: 7200s (2 hours) in 'full' preset Slack thread: https://postgres-ai.slack.com/archives/CCKQPEM09/p1768427779650849
1 parent 8228a81 commit 38e7e40

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

config/pgwatch-prometheus/metrics.yml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2506,6 +2506,85 @@ metrics:
25062506
- offsets_bytes
25072507
- status_code
25082508
statement_timeout_seconds: 15
2509+
sequence_overflow:
2510+
description: >
2511+
Monitors sequence-generated columns (serial/identity) for potential integer overflow risk.
2512+
Checks how close the current sequence value is to the maximum value allowed by the data type
2513+
(smallint: 32767, integer: 2147483647, bigint: 9223372036854775807).
2514+
Based on the approach described by Laurenz Albe at CYBERTEC:
2515+
https://www.cybertec-postgresql.com/en/integer-overflow-in-sequence-generated-primary-keys/
2516+
Reports both sequence data type and column data type percentages since they can differ
2517+
(e.g., bigint sequence feeding an integer column).
2518+
sqls:
2519+
11: |
2520+
select /* pgwatch_generated */
2521+
(extract(epoch from now()) * 1e9)::int8 as epoch_ns,
2522+
current_database() as tag_datname,
2523+
pn.nspname as tag_schema_name,
2524+
tbl.relname as tag_table_name,
2525+
att.attname as tag_column_name,
2526+
seq.relname as tag_sequence_name,
2527+
format_type(s.seqtypid, null) as tag_sequence_data_type,
2528+
format_type(att.atttypid, att.atttypmod) as tag_column_data_type,
2529+
coalesce(pg_sequence_last_value(seq.oid::regclass), s.seqstart) as current_value,
2530+
case format_type(s.seqtypid, null)
2531+
when 'smallint' then 32767
2532+
when 'integer' then 2147483647
2533+
when 'bigint' then 9223372036854775807
2534+
end as sequence_max_value,
2535+
case format_type(att.atttypid, null)
2536+
when 'smallint' then 32767
2537+
when 'integer' then 2147483647
2538+
when 'bigint' then 9223372036854775807
2539+
end as column_max_value,
2540+
round(
2541+
coalesce(pg_sequence_last_value(seq.oid::regclass), s.seqstart)::numeric /
2542+
(case format_type(s.seqtypid, null)
2543+
when 'smallint' then 32767
2544+
when 'integer' then 2147483647
2545+
when 'bigint' then 9223372036854775807
2546+
end) * 100, 2
2547+
) as sequence_percent_used,
2548+
round(
2549+
coalesce(pg_sequence_last_value(seq.oid::regclass), s.seqstart)::numeric /
2550+
(case format_type(att.atttypid, null)
2551+
when 'smallint' then 32767
2552+
when 'integer' then 2147483647
2553+
when 'bigint' then 9223372036854775807
2554+
end) * 100, 2
2555+
) as column_percent_used
2556+
from pg_depend d
2557+
join pg_class seq on seq.relkind = 'S' and seq.oid = d.objid
2558+
join pg_class tbl on tbl.relkind = 'r' and tbl.oid = d.refobjid
2559+
join pg_attribute att on att.attrelid = d.refobjid and att.attnum = d.refobjsubid
2560+
join pg_sequence s on s.seqrelid = seq.oid
2561+
join pg_namespace pn on pn.oid = tbl.relnamespace
2562+
where d.deptype = 'a'
2563+
and d.classid = 'pg_class'::regclass::oid
2564+
and pn.nspname not in ('pg_catalog', 'information_schema', 'pg_toast')
2565+
order by
2566+
greatest(
2567+
coalesce(pg_sequence_last_value(seq.oid::regclass), s.seqstart)::numeric /
2568+
(case format_type(s.seqtypid, null)
2569+
when 'smallint' then 32767
2570+
when 'integer' then 2147483647
2571+
when 'bigint' then 9223372036854775807
2572+
end),
2573+
coalesce(pg_sequence_last_value(seq.oid::regclass), s.seqstart)::numeric /
2574+
(case format_type(att.atttypid, null)
2575+
when 'smallint' then 32767
2576+
when 'integer' then 2147483647
2577+
when 'bigint' then 9223372036854775807
2578+
end)
2579+
) desc
2580+
limit 1000;
2581+
gauges:
2582+
- current_value
2583+
- sequence_max_value
2584+
- column_max_value
2585+
- sequence_percent_used
2586+
- column_percent_used
2587+
statement_timeout_seconds: 30
25092588

25102589
presets:
25112590
full:
@@ -2548,6 +2627,7 @@ presets:
25482627
redundant_indexes: 10800
25492628
unused_indexes: 7200
25502629
rarely_used_indexes: 10800
2630+
sequence_overflow: 7200
25512631
stats_reset: 3600
25522632
archive_lag: 15
25532633
pg_vacuum_progress: 30

0 commit comments

Comments
 (0)