Skip to content

Commit 681a9c5

Browse files
committed
Add the xmin horizon to running query display
The xmin horizon was added in 9.4 in commit [1]. [1] postgres/postgres@dd1a3bc
1 parent 15bffac commit 681a9c5

26 files changed

+166
-16
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
configuration.
1212
* Add a `y` command to copy focused query to the system clipboard, using
1313
OSC 52 escape sequence (#311).
14+
* Add the `xmin` column to the query display (#425).
1415

1516
### Fixed
1617

docs/man/pg_activity.1

+7
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ The running queries panel shows all running queries, transactions or backends
182182
.IP "\- \fBPID\fR: process id of the backend which executes the query;" 2
183183
.IX Item "- PID: process id of the backend which executes the query;"
184184
.PD 0
185+
.IP "\- \fBXMIN\fR: xmin horizon of the backend;" 2
186+
.IX Item "- XMIN: xmin horizon of the backend;"
185187
.IP "\- \fBDATABASE\fR: database specified in the connection string;" 2
186188
.IX Item "- DATABASE: database specified in the connection string;"
187189
.IP "\- \fBAPP\fR: application name specified in the connection string;" 2
@@ -364,6 +366,11 @@ required by another session. It shows following information:
364366
.Vb 1
365367
\& Enable/disable PID.
366368
.Ve
369+
.IP "\fB\-\-xmin\fR, \fB\-\-no\-xmin\fR" 2
370+
.IX Item "--xmin, --no-xmin"
371+
.Vb 1
372+
\& Enable/disable XMIN.
373+
.Ve
367374
.IP "\fB\-\-database\fR, \fB\-\-no\-database\fR" 2
368375
.IX Item "--database, --no-database"
369376
.Vb 1

docs/man/pg_activity.pod

+6
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ B<min duration> seconds. It displays the following information:
136136

137137
=item - B<PID>: process id of the backend which executes the query;
138138

139+
=item - B<XMIN>: xmin horizon of the backend;
140+
139141
=item - B<DATABASE>: database specified in the connection string;
140142

141143
=item - B<APP>: application name specified in the connection string;
@@ -322,6 +324,10 @@ required by another session. It shows following information:
322324

323325
Enable/disable PID.
324326

327+
=item B<--xmin>, B<--no-xmin>
328+
329+
Enable/disable XMIN.
330+
325331
=item B<--database>, B<--no-database>
326332

327333
Enable/disable DATABASE.

pgactivity/activities.py

+6
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ def sorted(processes: list[T], *, key: SortKey, reverse: bool = False) -> list[T
151151
>>> processes = [
152152
... LocalRunningProcess(
153153
... pid="6240",
154+
... xmin="1234",
154155
... application_name="pgbench",
155156
... database="pgbench",
156157
... user="postgres",
@@ -170,6 +171,7 @@ def sorted(processes: list[T], *, key: SortKey, reverse: bool = False) -> list[T
170171
... ),
171172
... LocalRunningProcess(
172173
... pid="6239",
174+
... xmin="2345",
173175
... application_name="pgbench",
174176
... database="pgbench",
175177
... user="postgres",
@@ -189,6 +191,7 @@ def sorted(processes: list[T], *, key: SortKey, reverse: bool = False) -> list[T
189191
... ),
190192
... LocalRunningProcess(
191193
... pid="6228",
194+
... xmin="3456",
192195
... application_name="pgbench",
193196
... database="pgbench",
194197
... user="postgres",
@@ -225,6 +228,7 @@ def sorted(processes: list[T], *, key: SortKey, reverse: bool = False) -> list[T
225228
>>> processes = [
226229
... LocalRunningProcess(
227230
... pid="6240",
231+
... xmin="1234",
228232
... application_name="pgbench",
229233
... database="pgbench",
230234
... user="postgres",
@@ -244,6 +248,7 @@ def sorted(processes: list[T], *, key: SortKey, reverse: bool = False) -> list[T
244248
... ),
245249
... LocalRunningProcess(
246250
... pid="6239",
251+
... xmin="2345",
247252
... application_name="pgbench",
248253
... database="pgbench",
249254
... user="postgres",
@@ -263,6 +268,7 @@ def sorted(processes: list[T], *, key: SortKey, reverse: bool = False) -> list[T
263268
... ),
264269
... LocalRunningProcess(
265270
... pid="6228",
271+
... xmin="3456",
266272
... application_name="pgbench",
267273
... database="pgbench",
268274
... user="postgres",

pgactivity/cli.py

+1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ def get_parser() -> argparse.ArgumentParser:
221221
"These options may be used hide some columns from the processes table.",
222222
)
223223
flag(group, "--pid", dest="pid", feature="PID")
224+
flag(group, "--xmin", dest="xmin", feature="XMIN")
224225
flag(group, "--database", dest="database", feature="DATABASE")
225226
flag(group, "--user", dest="user", feature="USER")
226227
flag(group, "--client", dest="client", feature="CLIENT")

pgactivity/config.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ class Flag(enum.Flag):
6262
"""Column flag.
6363
6464
>>> Flag.names()
65-
['database', 'appname', 'client', 'user', 'cpu', 'mem', 'read', 'write', 'time', 'wait', 'relation', 'type', 'mode', 'iowait', 'pid']
65+
['database', 'appname', 'client', 'user', 'cpu', 'mem', 'read', 'write', 'time', 'wait', 'relation', 'type', 'mode', 'iowait', 'pid', 'xmin']
6666
>>> Flag.all() # doctest: +ELLIPSIS
67-
<Flag...: 32767>
67+
<Flag...: 65535>
6868
"""
6969

7070
DATABASE = enum.auto()
@@ -82,6 +82,7 @@ class Flag(enum.Flag):
8282
MODE = enum.auto()
8383
IOWAIT = enum.auto()
8484
PID = enum.auto()
85+
XMIN = enum.auto()
8586

8687
@classmethod
8788
def names(cls) -> list[str]:
@@ -131,6 +132,7 @@ def load(
131132
user: bool | None,
132133
wait: bool | None,
133134
write: bool | None,
135+
xmin: bool | None,
134136
**kwargs: Any,
135137
) -> Flag:
136138
"""Build a Flag value from command line options."""
@@ -150,6 +152,7 @@ def load(
150152
(user, cls.USER),
151153
(wait, cls.WAIT),
152154
(write, cls.WRITE),
155+
(xmin, cls.XMIN),
153156
):
154157
if opt is True:
155158
flag |= value

pgactivity/profiles/minimal.conf

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ show_instance = no
33
show_system = no
44
show_workers = no
55

6+
[xmin]
7+
hidden = yes
8+
69
[database]
710
hidden = yes
811

pgactivity/profiles/narrow.conf

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
[xmin]
2+
hidden = yes
3+
14
[database]
25
hidden = yes
36

pgactivity/profiles/wide.conf

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
[xmin]
2+
hidden = no
3+
14
[database]
25
hidden = no
36

pgactivity/queries/get_pg_activity_oldest.sql

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
-- Get data from pg_activity before pg 9.2
22
SELECT
33
a.procpid AS pid,
4+
NULL AS xmin,
45
'<unknown>' AS application_name,
56
a.datname AS database,
67
a.client_addr AS client,

pgactivity/queries/get_pg_activity_post_090200.sql

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
-- NEW pg_stat_activity.procpid => pg_stat_activity.pid
44
SELECT
55
a.pid AS pid,
6+
NULL AS xmin,
67
a.application_name AS application_name,
78
a.datname AS database,
89
a.client_addr AS client,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
-- Get data from pg_activity from pg 9.4 to 9.6
2+
-- NEW pg_stat_activity.backend_xmin
3+
SELECT
4+
a.pid AS pid,
5+
a.backend_xmin AS xmin,
6+
a.application_name AS application_name,
7+
a.datname AS database,
8+
a.client_addr AS client,
9+
EXTRACT(epoch FROM (NOW() - a.{duration_column})) AS duration,
10+
a.wait_event AS wait,
11+
a.usename AS user,
12+
a.state AS state,
13+
a.query AS query,
14+
pg_catalog.pg_encoding_to_char(b.encoding) AS encoding,
15+
NULL AS query_leader_pid,
16+
false AS is_parallel_worker
17+
FROM
18+
pg_stat_activity a
19+
LEFT OUTER JOIN pg_database b ON a.datid = b.oid
20+
WHERE
21+
state <> 'idle'
22+
AND pid <> pg_backend_pid()
23+
AND CASE WHEN {min_duration} = 0
24+
THEN true
25+
ELSE extract(epoch from now() - {duration_column}) > %(min_duration)s
26+
END
27+
AND CASE WHEN {dbname_filter} IS NULL THEN true
28+
ELSE a.datname ~* %(dbname_filter)s
29+
END
30+
ORDER BY
31+
EXTRACT(epoch FROM (NOW() - a.{duration_column})) DESC;

pgactivity/queries/get_pg_activity_post_090600.sql

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
-- NEW pg_stat_activity.waiting => pg_stat_activity.wait_event
44
SELECT
55
a.pid AS pid,
6+
a.backend_xmin AS xmin,
67
a.application_name AS application_name,
78
a.datname AS database,
89
a.client_addr AS client,

pgactivity/queries/get_pg_activity_post_100000.sql

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
-- NEW pg_activity.backend_type
44
SELECT
55
a.pid AS pid,
6+
a.backend_xmin AS xmin,
67
a.application_name AS application_name,
78
a.datname AS database,
89
a.client_addr AS client,

pgactivity/queries/get_pg_activity_post_110000.sql

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
-- NEW pg_activity.backend_type value for 'parallel worker'
33
SELECT
44
a.pid AS pid,
5+
a.backend_xmin AS xmin,
56
a.application_name AS application_name,
67
a.datname AS database,
78
a.client_addr AS client,

pgactivity/queries/get_pg_activity_post_130000.sql

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
-- NEW pg_activity.leader_pid
33
SELECT
44
a.pid AS pid,
5+
a.backend_xmin AS xmin,
56
a.application_name AS application_name,
67
a.datname AS database,
78
a.client_addr AS client,

pgactivity/types.py

+8
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,17 @@ def add_column(
465465
sort_key=SortKey.write,
466466
transform=utils.naturalsize,
467467
)
468+
add_column(
469+
Flag.XMIN,
470+
key="xmin",
471+
min_width=8,
472+
default_color="cyan",
473+
)
468474

469475
columns_key_by_querymode: Mapping[QueryMode, list[str]] = {
470476
QueryMode.activities: [
471477
"pid",
478+
"xmin",
472479
"database",
473480
"application_name",
474481
"user",
@@ -916,6 +923,7 @@ def from_bytes(
916923
class RunningProcess(BaseProcess):
917924
"""Process for a running query."""
918925

926+
xmin: int
919927
wait: bool | None | str
920928
query_leader_pid: int | None
921929
is_parallel_worker: bool

pgactivity/utils.py

+12-9
Original file line numberDiff line numberDiff line change
@@ -221,24 +221,24 @@ def csv_write(
221221
"""Store process list into CSV file.
222222
223223
>>> processes = [
224-
... {'pid': 25199, 'application_name': '', 'database': 'pgbench', 'user': None,
224+
... {'pid': 25199, 'xmin': 1234, 'application_name': '', 'database': 'pgbench', 'user': None,
225225
... 'client': 'local', 'cpu': 0.0, 'mem': 0.6504979545924837,
226226
... 'read': 0.0, 'write': 0.0, 'state': 'active',
227227
... 'query': 'autovacuum: VACUUM ANALYZE public.pgbench_tellers',
228228
... 'duration': 0.348789, 'wait': False,
229229
... 'io_wait': False, 'is_parallel_worker': False},
230-
... {'pid': 25068, 'application_name': 'pgbench', 'database': None,
230+
... {'pid': 25068, 'xmin': 2345, 'application_name': 'pgbench', 'database': None,
231231
... 'user': 'postgres', 'client': 'local', 'cpu': 0.0, 'mem': 2.4694780629380646,
232232
... 'read': 278536.76590087387, 'write': 835610.2977026217,
233233
... 'state': 'idle in transaction',
234234
... 'query': 'INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (625, 87, 4368910, -341, CURRENT_TIMESTAMP);',
235235
... 'duration': 0.000105, 'wait': False, 'io_wait': False,
236236
... 'is_parallel_worker': False},
237-
... {'pid': 25379, 'application_name': 'pgbench', 'database': 'pgbench',
237+
... {'pid': 25379, 'xmin': 3456, 'application_name': 'pgbench', 'database': 'pgbench',
238238
... 'user': 'postgres', 'client': 'local', 'state': 'active',
239239
... 'query': 'UPDATE pgbench_branches SET bbalance = bbalance + -49 WHERE bid = 73;',
240240
... 'duration': 0, 'wait': False},
241-
... {'pid': 25392, 'application_name': 'pgbench', 'database': 'pgbench',
241+
... {'pid': 25392, 'xmin': 4567, 'application_name': 'pgbench', 'database': 'pgbench',
242242
... 'user': 'postgres', 'client': 'local', 'state': 'active',
243243
... 'query': 'BEGIN;', 'duration': 0, 'wait': False}
244244
... ]
@@ -249,11 +249,11 @@ def csv_write(
249249
... _ = f.seek(0)
250250
... content = f.read()
251251
>>> print(content, end="") # doctest: +ELLIPSIS
252-
datetimeutc;pid;database;appname;user;client;cpu;memory;read;write;duration;wait;io_wait;state;query
253-
"...-...-...T...Z";"25199";"pgbench";"";"None";"local";"0.0";"0.6504979545924837";"0.0";"0.0";"0.348789";"N";"N";"active";"autovacuum: VACUUM ANALYZE public.pgbench_tellers"
254-
"...-...-...T...Z";"25068";"";"pgbench";"postgres";"local";"0.0";"2.4694780629380646";"278536.76590087387";"835610.2977026217";"0.000105";"N";"N";"idle in transaction";"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (625, 87, 4368910, -341, CURRENT_TIMESTAMP);"
255-
"...-...-...T...Z";"25379";"pgbench";"pgbench";"postgres";"local";"N/A";"N/A";"N/A";"N/A";"0";"N";"N/A";"active";"UPDATE pgbench_branches SET bbalance = bbalance + -49 WHERE bid = 73;"
256-
"...-...-...T...Z";"25392";"pgbench";"pgbench";"postgres";"local";"N/A";"N/A";"N/A";"N/A";"0";"N";"N/A";"active";"BEGIN;"
252+
datetimeutc;pid;xmin;database;appname;user;client;cpu;memory;read;write;duration;wait;io_wait;state;query
253+
"...-...-...T...Z";"25199";"1234";"pgbench";"";"None";"local";"0.0";"0.6504979545924837";"0.0";"0.0";"0.348789";"N";"N";"active";"autovacuum: VACUUM ANALYZE public.pgbench_tellers"
254+
"...-...-...T...Z";"25068";"2345";"";"pgbench";"postgres";"local";"0.0";"2.4694780629380646";"278536.76590087387";"835610.2977026217";"0.000105";"N";"N";"idle in transaction";"INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (625, 87, 4368910, -341, CURRENT_TIMESTAMP);"
255+
"...-...-...T...Z";"25379";"3456";"pgbench";"pgbench";"postgres";"local";"N/A";"N/A";"N/A";"N/A";"0";"N";"N/A";"active";"UPDATE pgbench_branches SET bbalance = bbalance + -49 WHERE bid = 73;"
256+
"...-...-...T...Z";"25392";"4567";"pgbench";"pgbench";"postgres";"local";"N/A";"N/A";"N/A";"N/A";"0";"N";"N/A";"active";"BEGIN;"
257257
"""
258258

259259
def clean_str_csv(s: str) -> str:
@@ -266,6 +266,7 @@ def clean_str_csv(s: str) -> str:
266266
[
267267
"datetimeutc",
268268
"pid",
269+
"xmin",
269270
"database",
270271
"appname",
271272
"user",
@@ -292,6 +293,7 @@ def yn_na(value: bool | None) -> str:
292293
for p in procs:
293294
dt = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
294295
pid = p.get("pid", "N/A")
296+
xmin = p.get("xmin", "N/A")
295297
database = p.get("database", "N/A") or ""
296298
appname = p.get("application_name", "N/A")
297299
user = p.get("user", "N/A")
@@ -310,6 +312,7 @@ def yn_na(value: bool | None) -> str:
310312
[
311313
f'"{dt}"',
312314
f'"{pid}"',
315+
f'"{xmin}"',
313316
f'"{database}"',
314317
f'"{appname}"',
315318
f'"{user}"',

0 commit comments

Comments
 (0)