Skip to content

Commit aecd37d

Browse files
authored
Report pg_class xmin (#19218)
Add a postgresql.relation.xmin reporting the xmin of the relation's row in pg_class. This will allow to detect if and when a relation was modified through a DDL as most of DDL will modify the relation's pg_class. Other processes may modify pg_class, like ANALYZE to update tuple estimations. However, those updates are done in place and won't bump the row's xmin.
1 parent 685547d commit aecd37d

File tree

4 files changed

+34
-1
lines changed

4 files changed

+34
-1
lines changed

postgres/changelog.d/19218.added

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add postgresql.relation.xmin metric

postgres/datadog_checks/postgres/relationsmanager.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@
187187
pg_stat_get_vacuum_count(C.reltoastrelid),
188188
pg_stat_get_autovacuum_count(C.reltoastrelid),
189189
EXTRACT(EPOCH FROM age(CURRENT_TIMESTAMP, pg_stat_get_last_vacuum_time(C.reltoastrelid))),
190-
EXTRACT(EPOCH FROM age(CURRENT_TIMESTAMP, pg_stat_get_last_autovacuum_time(C.reltoastrelid)))
190+
EXTRACT(EPOCH FROM age(CURRENT_TIMESTAMP, pg_stat_get_last_autovacuum_time(C.reltoastrelid))),
191+
C.xmin
191192
FROM pg_class C
192193
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
193194
LEFT JOIN pg_locks L ON C.oid = L.relation AND L.locktype = 'relation'
@@ -234,6 +235,7 @@
234235
{'name': 'toast.autovacuumed', 'type': 'monotonic_count'},
235236
{'name': 'toast.last_vacuum_age', 'type': 'gauge'},
236237
{'name': 'toast.last_autovacuum_age', 'type': 'gauge'},
238+
{'name': 'relation.xmin', 'type': 'gauge'},
237239
],
238240
}
239241

postgres/metadata.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ postgresql.queries.time,count,,nanosecond,,"The total query execution time per q
116116
postgresql.relation.all_visible,gauge,,,,"Number of pages that are marked as all visible in the table's visibility map. This is only an estimation used by the planner and is updated by VACUUM or ANALYZE. This metric is tagged with db, schema, table, partition_of",0,postgres,relation all_visible,
117117
postgresql.relation.pages,gauge,,,,"Size of a table in pages (1 page == 8KB by default). This is only an estimation used by the planner and is updated by VACUUM or ANALYZE. This metric is tagged with db, schema, table, partition_of.",0,postgres,relation pages,
118118
postgresql.relation.tuples,gauge,,,,"Number of live rows in the table. This is only an estimation used by the planner and is updated by VACUUM or ANALYZE. If the table has never been vacuumed or analyze, -1 will be reported. This metric is tagged with db, schema, table, partition_of",0,postgres,relation tuples,
119+
postgresql.relation.xmin,gauge,,,,"Transaction ID of the latest relation's modification in pg_class. This metric is tagged with db, schema, table",0,postgres,relation xmin,
119120
postgresql.relation_size,gauge,,byte,,"The disk space used by the specified table. TOAST data, indexes, free space map and visibility map are not included. This metric is tagged with db, schema, table.",0,postgres,relation size,
120121
postgresql.replication.backend_xmin_age,gauge,,,,The age of the standby server's xmin horizon (relative to latest stable xid) reported by hot_standby_feedback.,-1,postgres,repl backend xmin,
121122
postgresql.replication.wal_flush_lag,gauge,,second,,Time elapsed between flushing recent WAL locally and receiving notification that this standby server has written and flushed it (but not yet applied it). This can be used to gauge the delay that synchronous_commit level on incurred while committing if this server was configured as a synchronous standby. Only available with postgresql 10 and newer.,-1,postgres,repl flush lag,

postgres/tests/test_relations.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,35 @@ def test_relations_metrics_regex(aggregator, integration_check, pg_instance):
197197
_check_metrics_for_relation_wo_index(aggregator, expected_tags[relation])
198198

199199

200+
@pytest.mark.integration
201+
@pytest.mark.usefixtures('dd_environment')
202+
def test_relations_xmin(aggregator, integration_check, pg_instance):
203+
pg_instance['relations'] = ['persons']
204+
205+
conn = _get_superconn(pg_instance)
206+
cursor = conn.cursor()
207+
cursor.execute("SELECT xmin FROM pg_class WHERE relname='persons'")
208+
start_xmin = float(cursor.fetchone()[0])
209+
210+
# Check that initial xmin metric match
211+
check = integration_check(pg_instance)
212+
check.check(pg_instance)
213+
expected_tags = _get_expected_tags(check, pg_instance, db=pg_instance['dbname'], table='persons', schema='public')
214+
aggregator.assert_metric('postgresql.relation.xmin', count=1, value=start_xmin, tags=expected_tags)
215+
aggregator.reset()
216+
217+
# Run multiple DDL modifying the persons relation which will increase persons' xmin in pg_class
218+
cursor.execute("ALTER TABLE persons REPLICA IDENTITY FULL;")
219+
cursor.execute("ALTER TABLE persons REPLICA IDENTITY DEFAULT;")
220+
cursor.close()
221+
conn.close()
222+
223+
check.check(pg_instance)
224+
225+
# xmin metric should be greater than initial xmin
226+
assert_metric_at_least(aggregator, 'postgresql.relation.xmin', lower_bound=start_xmin + 1, tags=expected_tags)
227+
228+
200229
@pytest.mark.integration
201230
@pytest.mark.usefixtures('dd_environment')
202231
def test_max_relations(aggregator, integration_check, pg_instance):

0 commit comments

Comments
 (0)