Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w6ges-004YCL-2m for pgsql-bugs@arkaria.postgresql.org; Sun, 29 Mar 2026 03:17:51 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w6gep-00FgFG-1s for pgsql-bugs@arkaria.postgresql.org; Sun, 29 Mar 2026 03:17:47 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1w6gep-00FgF4-0C for pgsql-bugs@lists.postgresql.org; Sun, 29 Mar 2026 03:17:47 +0000 Received: from mail-ed1-x529.google.com ([2a00:1450:4864:20::529]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w6gef-00000001aPQ-42Yp for pgsql-bugs@lists.postgresql.org; Sun, 29 Mar 2026 03:17:46 +0000 Received: by mail-ed1-x529.google.com with SMTP id 4fb4d7f45d1cf-66abe08cdf5so5274549a12.0 for ; Sat, 28 Mar 2026 20:17:37 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1774754255; cv=none; d=google.com; s=arc-20240605; b=ZTHSdN4lKz0HruWhENMXumzjutcVbimQOc1fH8mVQEfaSf7jhodaETLq3RfRmKxhvc L9Z2h4bEaDU61CoyCfAFp2Kbjp1tmQa99Y52/l09zVlcmqhjYpzrT0zGgFWKPwB8Jd/u mF9vc6DviLrjSukcfHBmjGKRvclotp1ExPfDR0qea+3EGGbSotMLuTCmYrs8W3huH51j 6XY73gDXOXn/Tk0ulB2ByWhBslhM5WN54B/DUm7LynqbhwrVZssu646gjS5hIGDZvz65 7rYofBJa73t/AQWCsjdXirReqOCDJA0ybWmd7akRvZ2kB/2xpRFnlccQvcy1vVu+3YMl 6Aug== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=CMk0vIJ4795Applj35LyKSqKVrPYJvEKeMG9z2jtZ1Y=; fh=rgNhvRC3Fv4x3CFuZ6LQ7j9vp7LaJglJk2xqiucPm5s=; b=XeeXUSV5NXtFURdmTyvOYMN8Vga0ev44dJ7xJXHrosPn1UUp3ZfTj+hHvNU7HsQMAg 3RSIz8c/5XNxICPn1iSHGMAo+SQ12ZjKbF/WjTsfKwC29wW9vCU9OKtXpP5f13i47nbI 7l1IsmGbeJ8SRnhoZU8eWAel3FA0qBDUemrF04PEqcUYfN+1k5ZouuTDYjwus82zw8hT 1urYddAM9x9INoqEa8gYTuekbcsCD+FpaQdLVuFs4wyPQ2RNd5/eQxuU4KIhc7ca2y2p g7wnr+q8GIdvdoxzdmy4mc/75yojGhgwMFjybiMz9UYZdzcS44zFgCI7a0Y+hmMAM+4w ujAQ==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1774754255; x=1775359055; darn=lists.postgresql.org; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=CMk0vIJ4795Applj35LyKSqKVrPYJvEKeMG9z2jtZ1Y=; b=PlG7S7CetEjLKk990vxSFhtmAe1K1hqbFoyH8ft2Dh44vnTGuUVmq3dm2PSmk4HB+s NMIVqVEijQaOIJZha4EDX5k8/fhaE69Bs6r4ejwJQxckwSSuKCjHiQ6otCmiCTKh9KFM b5waImgFK7VJhqwj5kfucrxN9JIw5NePS9XtTks9LRAZ+chSCZFyPRej5sQbJVFwiwpG VPRNxWTN76Ow3MhHAC/lYP7gYEvNHetXJvJ8EZ8UNOExAg9fAE+gSTHuS3MM/bngX9OB jrRDsCiYmlwC3a5+2kg3DyIdmfahfa856FSn4IKqiqeFnkVdwrKCbmwmPPs9fapijyco IqVg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774754255; x=1775359055; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=CMk0vIJ4795Applj35LyKSqKVrPYJvEKeMG9z2jtZ1Y=; b=Yh+o2C0cCRZctXqH8cjRruws4EriD0L7psxJ3RNANx2z0IjyPv5UZi7Xi5mnWRrvUN pEZgSsOx8lWHNLScEq/MQTjL8CKWN8gtZnVhptZOoOD/41Z8pPpn5RDTrQEzWI5Z0WK+ 7DYxAsIzxvele28dCakCksBRJienJCOrbxe06kzUQP0MJPyq5qOe/fdGTLgW9JaNndom 90KTRaFpDv73XhoD1BouRvLKopFUKUs2ov62Jryvf+8b5psc2ikMebk5o31b0VpsgDD5 HLOJA/6vCEsLMsZlCm1c9ZK+h0G+KTjeUfNDKrqlzuFZkdhHL5xSnitmb9NvdDdZNuWm AFCg== X-Forwarded-Encrypted: i=1; AJvYcCUlkvISHYXAddda5OEJuzfUqe/+IB0MIKode5SCvtzwi9l/huuZYkzG9ckZsoy27+XzltZy6BamqWLL@lists.postgresql.org X-Gm-Message-State: AOJu0YxGU47tlGXLti20P7c1qTSuh4SuFISFN0ZfkSTIiAtDF3kccj1f w6aCGXab0V3igyr3PD9pODrwZ/lIjDo/AdiAU/hfvtnoJ9yYsB5+I2xWPzXIN30ibdfKuALcu+9 iEh3mbDcod3noHmhFXu6c3Xgf5ier5tTVXwE/LtXdhA== X-Gm-Gg: ATEYQzyfkmIHQSBJHI6MAHGV8Ht4Iay6s0Lww1GhMlBx8P037ZmpgyOmCL6nXq9fkmC UjSKMvyqRlTmSa8WRqN9g09Oygqq30/1cpQl0PspFGPL/X9V7OPBuZDL/H6zfXhSkw832AD+3// HAS/Yx2RtcTFUQ3G6ofsQeg+JBW4R3m97UQukhOh6BtclEc97S6Ewc8Tv2aNhE0zzjSQoR+2np2 g79gh7OnUJL2D78rfUUbfSL4/c26wL680PP05mCzcSKrh8wxAZ3BgoDVHqNdZYflRPnC/5C9KKL ts0v3DitgRJashhxGqpbJws7Rf4kbWAmI5xnZwi5X/dIEyS7iv2H7YmYNLgz271yM1/9M0iVE6d m/pk58IT8kle/uQ66Jtk= X-Received: by 2002:a05:6402:21d8:b0:659:428f:a6c0 with SMTP id 4fb4d7f45d1cf-66b28b5d070mr4008983a12.20.1774754255213; Sat, 28 Mar 2026 20:17:35 -0700 (PDT) MIME-Version: 1.0 References: <19439-8f02018f9fc3a240@postgresql.org> In-Reply-To: From: Xuneng Zhou Date: Sun, 29 Mar 2026 11:17:23 +0800 X-Gm-Features: AQROBzB1k3Nw7tvsr1ojtKg5ivqGWKt7NdFruIaZWNTQtnMqRhP7GDFmK3eKLQY Message-ID: Subject: Re: BUG #19439: pg_stat_xact_user_tables stat not currect during the transaction To: klemen.kobau@gmail.com, pgsql-bugs@lists.postgresql.org Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Sat, Mar 28, 2026 at 1:47=E2=80=AFPM Xuneng Zhou = wrote: > > On Fri, Mar 27, 2026 at 10:15=E2=80=AFAM Xuneng Zhou wrote: > > > > Hi klemen, > > > > Thanks for the report. > > > > On Fri, Mar 27, 2026 at 5:36=E2=80=AFAM PG Bug reporting form > > wrote: > > > > > > The following bug has been logged on the website: > > > > > > Bug reference: 19439 > > > Logged by: klemen kobau > > > Email address: klemen.kobau@gmail.com > > > PostgreSQL version: 18.0 > > > Operating system: Linux (EndeavorOS) > > > Description: > > > > > > I am running postgres:18.0 in a docker container, the configuration i= s as > > > follows: > > > > > > postgres: > > > image: postgres:18.0 > > > command: [ > > > "postgres", > > > "-N", "200", > > > "-c", "max_prepared_transactions=3D100", > > > "-c", "wal_level=3Dlogical", > > > "-c", "max_wal_senders=3D10", > > > "-c", "max_replication_slots=3D20", > > > "-c", "wal_keep_size=3D10", > > > "-c", "max_slot_wal_keep_size=3D1024" > > > ] > > > environment: > > > POSTGRES_USER: postgres > > > POSTGRES_PASSWORD: postgres > > > POSTGRES_DB: postgres > > > TZ: UTC > > > PGTZ: UTC > > > ports: > > > - 5432:5432 > > > volumes: > > > - postgres_data:/var/lib/postgresql > > > > > > I use psql version 18.3. > > > > > > I run the following: > > > > > > psql -h localhost -p 5432 -U postgres -d postgres -c " > > > -- Transaction 1: insert 1 row, check stats, commit > > > BEGIN; > > > CREATE TABLE IF NOT EXISTS temp.xact_test (id serial PRIMARY KEY, val= text); > > > INSERT INTO temp.xact_test (val) VALUES ('a'); > > > SELECT relname, n_tup_ins FROM pg_stat_xact_user_tables WHERE relname= =3D > > > 'xact_test'; > > > COMMIT; > > > > > > -- Transaction 2: insert 1 row, check stats > > > BEGIN; > > > INSERT INTO temp.xact_test (val) VALUES ('b'); > > > SELECT relname, n_tup_ins FROM pg_stat_xact_user_tables WHERE relname= =3D > > > 'xact_test'; > > > ROLLBACK; > > > > > > -- Cleanup > > > DROP TABLE temp.xact_test; > > > " > > > > > > And the output is > > > > > > BEGIN > > > CREATE TABLE > > > INSERT 0 1 > > > relname | n_tup_ins > > > -----------+----------- > > > xact_test | 1 > > > (1 row) > > > > > > COMMIT > > > BEGIN > > > INSERT 0 1 > > > relname | n_tup_ins > > > -----------+----------- > > > xact_test | 2 > > > (1 row) > > > > > > ROLLBACK > > > DROP TABLE > > > > I can also reproduce this behavior on HEAD. > > > > > This is not what I would expect from reading > > > https://www.postgresql.org/docs/current/monitoring-stats.html#MONITOR= ING-STATS-VIEWS, > > > where it states > > > > > > > A transaction can also see its own statistics (not yet flushed out = to the > > > shared memory statistics) in the views pg_stat_xact_all_tables, > > > pg_stat_xact_sys_tables, pg_stat_xact_user_tables, and > > > pg_stat_xact_user_functions. These numbers do not act as stated above= ; > > > instead they update continuously throughout the transaction. > > > > > > based on this, I would expect that the numbers are updated each time = an > > > insert happens and that they are isolated per transaction. > > > > > > > This snippet of the doc feels somewhat ambiguous. The current behavior > > does not seem fully consistent with wording such as =E2=80=9Cthroughout= the > > transaction.=E2=80=9D It also seems more appropriate for these table vi= ews to > > reflect stats within their own scopes, rather than cumulative values > > spanning txn boundaries. > > > > I=E2=80=99ve looked into this issue and 'd like to propose a patch to add= ress > it. Feedback is very welcome. > > --- Root cause > > Since the stats subsystem was rewritten (commit 5891c7a8), each > backend keeps a hash of pending stats entries that persist across > transaction boundaries. Entries that flush successfully are deleted > from the backend-local pending list; entries not flushed remain > pending with their accumulated counters. Flushing is driven by > pgstat_report_stat(), called from the ReadyForQuery path, and subject > to a rate limiter. > > The pg_stat_xact_* views read these pending entries directly via > find_tabstat_entry() / find_funcstat_entry(). Both functions return > the raw accumulated counters without per-transaction scoping. > > The bug is deterministic when multiple top-level transactions are > processed within a single simple-query message, because there is no > ReadyForQuery boundary between the transactions and therefore no > opportunity to flush and remove the pending entry. > > --- Fix > > The patch introduces a per-entry "transaction baseline" =E2=80=94 a lazy > snapshot of the counters taken the first time an entry is touched in > each new top-level transaction. The accessor functions > (find_tabstat_entry, find_funcstat_entry) then subtract the baseline > from the current counters, yielding only the current transaction's > delta. > > The baseline is keyed by MyProc->vxid.lxid. A static inline helper, > pgstat_ensure_xact_baseline(), is called at every nontransactional > counter-increment site (the 7 event-counter macros in pgstat.h and the > 4 non-inline counter functions in pgstat_relation.c). After the first > call per entry per transaction, the check reduces to a single integer > comparison with a predictably not-taken branch. > > For function stats, a new PgStat_FunctionPending wrapper struct embeds > PgStat_FunctionCounts at offset 0 with the baseline fields appended, > so the flush callback requires only a trivial cast change. > > --- Testing > > The regression test is a TAP test under src/bin/psql/t/ that uses psql > -c to send multi-statement strings as single simple-query messages. > src/bin/psql/t look like the right existing harness for the primary > regression because psql -c sends the whole multi-statement string as > one simple-query message via simple_action_list dispatch. By contrast, > ordinary pg_regress SQL files are executed by psql in file/stdin mode, > which runs through MainLoop() and dispatches top-level statements one > at a time via SendQuery(), creating ReadyForQuery boundaries that > could mask the bug. > > The test covers three scenarios: table n_tup_ins and seq_scan > counters, and function calls =E2=80=94 each verifying isolation across > consecutive top-level transactions within a single message. > postgres % cat output.txt BEGIN CREATE TABLE INSERT 0 1 relname | n_tup_ins -----------+----------- xact_test | 1 (1 row) COMMIT BEGIN INSERT 0 1 relname | n_tup_ins -----------+----------- xact_test | 2 (1 row) ROLLBACK DROP TABLE I can also reproduce this behavior in v13. So this issue pre-existed the major refactor 5891c7a8. -- Best, Xuneng