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.94.2) (envelope-from ) id 1vBm4o-0050rv-Ew for pgsql-hackers@arkaria.postgresql.org; Thu, 23 Oct 2025 03:33:22 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1vBm4m-003NR1-C3 for pgsql-hackers@arkaria.postgresql.org; Thu, 23 Oct 2025 03:33:19 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1vBm4l-003NQO-IQ for pgsql-hackers@lists.postgresql.org; Thu, 23 Oct 2025 03:33:19 +0000 Received: from fhigh-b5-smtp.messagingengine.com ([202.12.124.156]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vBm4g-003j5i-2I for pgsql-hackers@lists.postgresql.org; Thu, 23 Oct 2025 03:33:17 +0000 Received: from phl-compute-06.internal (phl-compute-06.internal [10.202.2.46]) by mailfhigh.stl.internal (Postfix) with ESMTP id B9B717A0149; Wed, 22 Oct 2025 23:33:11 -0400 (EDT) Received: from phl-mailfrontend-01 ([10.202.2.162]) by phl-compute-06.internal (MEProxy); Wed, 22 Oct 2025 23:33:11 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paquier.xyz; h= cc:cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm2; t=1761190391; x=1761276791; bh=dSwJP+y2U1 /of41kii6eNyYZSsh4OllmKYwn8r2A3QU=; b=dbZmGz2269khchZB7gmzxjlA91 Vm2IglRPiPCxh5R21rXAf7eEC6K5TiWesyASmBHuKUc1ciaLW8K0GJsGd5fx/W8a foIi3rnqnEa1oWR6mMHa8AWU91WREvkD20nepDQjAYkU0vJUs5Wr6pkQZuQ2IbtL kRW9cHHarDnjOj3cR23L9oNRZb8VE6Ak9iWGyQz1dRVUSgBRUGnvEjTGvydi9J0s tbt5cNc0TGo5SotgN/ngvuiMXrz6W54wL/n3sZyQXAdIbALOACJFW4rKrdzEDLC9 KEPoRxw9hH5B1VhuP4rBxEnKSmPvCRkLBGRfciyPqGGPaPfKKdN0GCYFzfZg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm2; t= 1761190391; x=1761276791; bh=dSwJP+y2U1/of41kii6eNyYZSsh4OllmKYw n8r2A3QU=; b=GtjYbsitwG3R8KRXLrdXI+KI3hzbdW+ZAMzOj9+V0ezFN7+4F5d nFq9LCBxB2WMynbMwVMrE/FfvRJbfNxhRiob8X6B/maGQt0D5nsagA7lzY+PWH2R DA6+OwNv0jFQhAo4We1j2SUFnRGBa0Xe+Uo221Ta4+nzf/LLeYhHw4mU/g6UJqxI iAVtegzWckdCmcgjMgLvcc7Se4Pq+pvN2Uz/r/eSWKI15dAb/UaILAJRaCsuIopX vc713Uj23yHWcNffo4018vnxVis+ajcMpctRK10cpbXsaQu76MsoJxcN+qeF3TKz s+3Yp5MUV4RY3am9m01w0OrrbBX7pQgL6Qw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddugeehfeekucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnegfrh hlucfvnfffucdljedtmdenucfjughrpeffhffvvefukfhfgggtuggjsehgtderredttddv necuhfhrohhmpefoihgthhgrvghlucfrrghquhhivghruceomhhitghhrggvlhesphgrqh huihgvrhdrgiihiieqnecuggftrfgrthhtvghrnhepteelieefudffhffhtdetleeggeeg fffhkeeuveetiefgudduvedutefggeeivdejnecuvehluhhsthgvrhfuihiivgeptdenuc frrghrrghmpehmrghilhhfrhhomhepmhhitghhrggvlhesphgrqhhuihgvrhdrgiihiidp nhgspghrtghpthhtohepvddpmhhouggvpehsmhhtphhouhhtpdhrtghpthhtohepshgrmh himhhsvghihhesghhmrghilhdrtghomhdprhgtphhtthhopehpghhsqhhlqdhhrggtkhgv rhhssehlihhsthhsrdhpohhsthhgrhgvshhqlhdrohhrgh X-ME-Proxy: Feedback-ID: i0fe9450f:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 22 Oct 2025 23:33:10 -0400 (EDT) Date: Thu, 23 Oct 2025 12:33:02 +0900 From: Michael Paquier To: Sami Imseih Cc: PostgreSQL Hackers Subject: Re: [Proposal] Adding callback support for custom statistics kinds Message-ID: References: MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="cKk/QMdlWCARx//q" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --cKk/QMdlWCARx//q Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Wed, Oct 22, 2025 at 03:24:11PM -0500, Sami Imseih wrote: > I'd like to propose $SUBJECT to serialize additional per-entry data beyond > the standard statistics entries. Currently, custom statistics kinds can s= tore > their standard entry data in the main "pgstat.stat" file, but there is no > mechanism for extensions to persist extra data stored in the entry. A com= mon > use case is extensions that register a custom kind and, besides > standard counters, > need to track variable-length data stored in a dsa_pointer. Thanks for sending a proposal in this direction. > A concrete use case is pg_stat_statements. If it were to use custom > stats kinds to track statement counters, it could also track query text > stored in DSA. The callbacks allow saving the query text referenced by the > dsa_pointer and restoring it after a clean shutdown. Since DSA > (and more specifically DSM) cannot be attached by the postmaster, an > extension cannot use "on_shmem_exit" or "shmem_startup_hook" > to serialize or restore this data. This is why pgstat handles > serialization during checkpointer shutdown and startup, allowing a single > backend to manage it safely. Agreed that it would be better to split the query text in a file of its own and now bloat the "main" pgstats file with this data, A real risk is that many PGSS entries with a bunch of queries would cause the file to be just full of the PGSS contents. > I considered adding hooks to the existing pgstat code paths > (pgstat_before_server_shutdown, pgstat_discard_stats, and > pgstat_restore_stats), but that felt too unrestricted. Using per-kind > callbacks provides more control. Per-kind callbacks to control all that makes sense here. > There are already "to_serialized_name" and "from_serialized_name" > callbacks used to store and read entries by "name" instead of > "PgStat_HashKey", currently used by replication slot stats. Those > remain unchanged, as they serve a separate purpose. >=20 > Other design points: >=20 > 1. Filenames use "pgstat..stat" based on the numeric kind ID. > This avoids requiring extensions to provide names and prevents issues > with spaces or special characters. Hmm. Is that really what we want here? This pretty says that one single custom kind would never be able use multiple files, ever. > 2. Both callbacks must be registered together. Serializing without > deserializing would leave orphaned files behind, and I cannot think of a > reason to allow this. Hmm. Okay. > 3. "write_chunk", "read_chunk", "write_chunk_s", and > "read_chunk_s" are renamed to "pgstat_write_chunk", etc., and > moved to "pgstat_internal.h" so extensions can use them without > re-implementing these functions. Exposing the write and read chunk APIs and renaming them sounds good here, designed as they are now with a FILE* defined by the caller. It's good to share these for consistency across custom and built-in stats kinds. > 4. These callbacks are valid only for custom, variable-numbered statistics > kinds. Custom fixed kinds may not benefit, but could be considered in the > future. Pushing custom data for fixed-sized stats may be interesting, though like you I am not sure what a good use-case would look like. So discarding this case for now sounds fine to me. > Attached 0001 is the proposed change, still in POC form. Hmm. I would like to propose something a bit more flexible, refactoring and reusing some of the existing callbacks, among the following lines: - Rather than introducing a second callback able to do more serialization work, let's expand a bit the responsibility of to_serialized_name and from_serialized_name to be able to work in a more extended way, renaming them to "to/from_serialized_entry", which are now limited to return a NameData with pgstat.c enforcing the data written to the pgstats to be of NAMEDATALEN. The idea would be to let the callbacks push some custom data where they want. - The to_serialized_name path of pgstat_write_statsfile() would then be changed as follows: -- push a PGSTAT_FILE_ENTRY_NAME -- Write the key write_chunk_s. -- Call the callback to push some custom per-entry data. -- Finish with the main chunk of data, of size pgstat_get_entry_len(). - The fd or FILE* of the "main" pgstats file should be added as argument of both routines (not mandatory, but we are likely going to need that if we want to add more "custom" data in the main pgstats file before writing or reading a chunk). For example, for a PGSS text file, we would likely write two fields to the main data file: an offset and a length to be able to retrieve a query string, from a secondary file.=20 - FDs where the data is written while we are in the to/from serialize can be handled within the code paths specific to the stats kind code. The first time a serialized callback of a stats kind is called, the extra file(s) is(are) opened. This may come at the cost of one new callback: at the end of the read and writes of the stats data, we would need an extra look that's able to perform cleanup actions, which would be here to make sure that the fds opened for the extra files are closed when we are done. The close of each file is equivalent to the pgstat_close_file() done in the patch, except that we'd loop over a callback that would do the cleanup job once we are done reading or writing a file. One step that can be customized in this new "end" callback is if a stats kind may decide to unlink() a previous file, as we do for the main pgstats file, or keep one or more files around. That would be up to the extension developer. We should be able to reuse or rework reset_all_cb() with a status given to it, depending on if we are dealing with a failure or a success path. Currently, reset_all_cb() is only used in a failure path, the idea would be to extend it for the success case. > The second patch > contains tests in "injection_points" to demonstrate this proposal, and i= s not > necessarily intended for commit. Having coverage for these kinds of APIs is always good, IMO. We need coverage for extension code. -- Michael --cKk/QMdlWCARx//q Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEG72nH6vTowiyblFKnvQgOdbyQH0FAmj5oe4ACgkQnvQgOdby QH1apxAAjUgrUN5GGq6lTuFQJ/LgooJCpZwGtI8pNb6yCExuIeytLLkCGGp2NCBb sBCk9cE3rL1Sq0FPvIZHr4tnB214vMpVoIo8tpK8FqE+cV7nckZTZxzfRoygL5y0 gM1zAZpDoXPqmWBvvUY8GcHmLNS+orBjPOWZ6p8Gtwd/tgTCrFtHjjfQnlPU9RpY 53346EsftlZXryUtGmCfjRyiP3YfesHOCOq5Jz3pXqTX4ZjlPG/HmoRtJxPAgD8r QPCjqC8aDaLFblJv6S0g6UFuOYD9BA4CGVZybh5VwYeyqMDbPZZcVBGiA5n10p2V lx4yeg1/1i1Y5yOfbz0afhK4Z1kp8iEGKPA7HOVhHNHcxsMLTpWGJ3OpjJEd4BH7 Zbm4S2CSkjDgOCJVw9x2rQrQw8O59bpZX3Eqgrx0THFBLeI4nac57ZRSLBbaeeiw BO961yUeOcns/wqLDtP2RcqVIt5mlSjbw9BpzXybuTxh+r5SsNdZbSLqq9qUC+8B MGFqqrPnyFqW9i4rouKayY8+OX4S7eI1UmG+L+ZDuGGA431J6b+z0Gxhri6PVDoy yO2DJTIjOc0+6n48MI7mnT5dPVsK4/xYvk5AvKxgKyAkgysqpzeKouaT59hm3Mva Bb5THQcVlt6T6bcHU7V4VXxhDxqsg88CDCCdvSd/DqD73A79xJQ= =zkmi -----END PGP SIGNATURE----- --cKk/QMdlWCARx//q--