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 1w579V-002uLA-25 for pgsql-hackers@arkaria.postgresql.org; Tue, 24 Mar 2026 19:10:57 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1w578V-008e7J-0C for pgsql-hackers@arkaria.postgresql.org; Tue, 24 Mar 2026 19:09:55 +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 1w578U-008e7A-25 for pgsql-hackers@lists.postgresql.org; Tue, 24 Mar 2026 19:09:55 +0000 Received: from mail-lf1-x130.google.com ([2a00:1450:4864:20::130]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1w578T-00000000q5l-0qBf for pgsql-hackers@lists.postgresql.org; Tue, 24 Mar 2026 19:09:54 +0000 Received: by mail-lf1-x130.google.com with SMTP id 2adb3069b0e04-59e4a04f059so3839967e87.2 for ; Tue, 24 Mar 2026 12:09:53 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1774379390; cv=none; d=google.com; s=arc-20240605; b=hMt4SMJrZm5Ulhbam3Z0J1c4Xgvn6uPnBIDy5B94UnPWRLLvvmY/S6gu7jYZkAD9kz LyYAKOIy0gvej2NsiBBhYCfyKUSOsP4N2C3zadmTY7lxCkDFS30N0hbfvvC9hKS0tC1P b8TfVlVxz+P04hgE9GoiISRDm2SJbtr7Au6tIsDcwcXmr5iHDi1VIIUSJTvpqzUECe1Y qZP8dms7AovFKAlHZ88rJO9WvhkOzYvwglVlLT9caK90D4q49L2EH7Qdp8HZf6eGbD4n ByXSqwYMxniI3P/ARuasZ03vy1equsolJDblBKU6As2ModjHWeEA/gnLNDueJ8nu4o2p dZMg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=py1IgGr0Jl3EYQPWTw7FP7wkKpTOXOH5+SEW6y+dH7w=; fh=u/EQOMM1kjvmNw96lRKct6+jYOR8T2ujO2D0JTL/lQw=; b=A7cmRJCCwouQLsLh0+xKIRzJ2HQUCvrukUZAb/u2xNiWeTYwZqTSjfUSaGJBRQLBzg PREUR4JnnjJgTXofjXuZWq0Mgen6e3Zjip6+CIJ+U2lIAG6bHLOokd/A89K9B0wqUOyX 2a8iEXrKM9uBLxzcF62bTvSM+SpmvdtjOBLpitmEhxs6mXyaMjGoDoztBH/CNZg8NVm6 ZCvuFK3kBYqmHolGgB6y4LHm//r7CFih1oWuySlHPTtQGaGzU6VMKtoWOPptWqDbNRKn 4iZwNBPDjt9MkkUX1LwvRamR33aqJpzd1Iab1/U8n35te0LvW+AGXdO2fUGt4XfgPUbw ghmQ==; 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=1774379390; x=1774984190; darn=lists.postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=py1IgGr0Jl3EYQPWTw7FP7wkKpTOXOH5+SEW6y+dH7w=; b=Vzx41a5Jg8uIuDwMU+W5zDcfMPJOb1I1QEVafxpe7GG0g9F7LjD/jvUD6wvqSFwlKx /Q3O1EsEYvShHzHQ9abZ6tMSwK8dCWhuyXvozgGbzEG1CkKjNA1lCTZbQhMlfAxq0M71 lyJ8Bcfr+DAqdkriseDFULeVegxwCFKB2fNte+SqxM93q5ZJX8dyA0PYSw0c720HtSIl CU/fh15yhXfoElWMRlIYpbfQcQr9Gbu+b33vq5vOKNxtlYcHbVMDaffXSn2hBbDv74WH go0sOc5cAQT0jNMM2ryTzOAiN2l/0VdlIj7G7QXbuGHMoSlYZdR/Rs1XjcL0ce8TEBpS RXTw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774379390; x=1774984190; h=content-transfer-encoding:cc: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=py1IgGr0Jl3EYQPWTw7FP7wkKpTOXOH5+SEW6y+dH7w=; b=FU5ITZCYLvxrufyj0sa0zjbg7NgiArSsgCxM1r199hw5pzxs4tkDNWK9fU9Pkbil6k kedlvoQDF2MIwJKF5wmF8qwrreGMAzwvgzQemCIL3BxVyzr3iixp152s1TXnIcYZeBCJ c4iVZG+NUVdx7x+ZDtBnCiji+tZg4njHYim1edKTtqltpfzo9+xwe63+vyyELMztunz2 67FXQN/mpt4FnNlUl4Yb3f/l//Xj8fnDjSl1SoX4aUQy8PwbB4tRgq6YJdK/QdS0YkTg I8KEhSYaRLETldQzlhaQghgQnOXzM9YgFJkXBgm3LN6SyQgxwHzOuFd3JqwvRJI5r45S A+Xw== X-Gm-Message-State: AOJu0YzQz9MsslHyAdYpN/2vQu65hrF0qymgjujRAYZp7Xufjw+FRySW VTm2++1qhMNsTME8Pha2qBc6aTz/kM6Kgg175lu2XL3d32uLzT5F3mLjQ1vVUs5StcccbWJHo+t a3mlbOP7TcaCdYm1eqJm/UDfkNPRxHMM= X-Gm-Gg: ATEYQzykgWjF/aiK6WZBLs1m40vfJGecYOwlzjFUgBmju06hGvZmZ5TAwaJnjZSTat5 K92zhWhZyUAPxMfIDGMeuQ8uWOrGM30blUfOzc2n0/fGfZPk+w1jjXnY2lqMCvoHxMCU3Qd2Btm CD0BH7B49fU3fKaQL4a0JRdCDIox4JJu7Tq5Yty9Oz6HrOEtac5I8/95gdRCkwuoIdU1t348Sjj f00kMf8SaKN5SQINSTqd0XGeVoKwB68RHymgP+1GeNHATeblfqKXh69upA+m/jHOzyo3DjuMsHo C8p3tH5WgHNbcV12SpjDz4CRSXvMCL59uWC2Ew== X-Received: by 2002:a05:6512:10c7:b0:5a2:9b44:1cb with SMTP id 2adb3069b0e04-5a29b97d3f6mr223569e87.2.1774379390152; Tue, 24 Mar 2026 12:09:50 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Masahiko Sawada Date: Tue, 24 Mar 2026 12:09:13 -0700 X-Gm-Features: AQROBzDiskE2UfFhj8p1AJbnN5yzX8o-avZq7RwVRDoVbDt-ePzeet_yIK9_VVk Message-ID: Subject: Re: pg_buffercache: Add per-relation summary stats To: Lukas Fittl Cc: PostgreSQL Hackers , Paul A Jungwirth , Khoa Nguyen 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 Hi Lukas, On Sat, Feb 28, 2026 at 3:59=E2=80=AFPM Lukas Fittl wrote= : > > Hi, > > See attached a patch that implements a new function, > pg_buffercache_relation_stats(), which returns per-relfilenode > statistics on the number of buffers, how many are dirtied/pinned, and > their avg usage count. Thank you for the proposal! Paul A Jungwirth, Khoa Nguyen, and I reviewed this patch through the Patch Review Workshop, and I'd like to share our comments. > > This can be used in monitoring scripts to know which relations are > kept in shared buffers, to understand performance issues better that > occur due to relations getting evicted from the cache. In our own > monitoring tool (pganalyze) we've offered a functionality like this > based on the existing pg_buffercache() function for a bit over a year > now [0], and people have found this very valuable - but it doesn't > work for larger database servers. > > Specifically, performing a query that gets this information can be > prohibitively expensive when using large shared_buffers, and even on > the default 128MB shared buffers there is a measurable difference: > > postgres=3D# WITH pg_buffercache_relation_stats AS ( > SELECT relfilenode, reltablespace, reldatabase, relforknumber, > COUNT(*) AS buffers, > COUNT(*) FILTER (WHERE isdirty) AS buffers_dirty, > COUNT(*) FILTER (WHERE pinning_backends > 0) AS buffers_pinned, > AVG(usagecount) AS usagecount_avg > FROM pg_buffercache > WHERE reldatabase IS NOT NULL > GROUP BY 1, 2, 3, 4 > > ) > SELECT * FROM pg_buffercache_relation_stats WHERE relfilenode =3D 2659; > > relfilenode | reltablespace | reldatabase | relforknumber | buffers | > buffers_dirty | buffers_pinned | usagecount_avg > -------------+---------------+-------------+---------------+---------+---= ------------+----------------+-------------------- > 2659 | 1663 | 5 | 0 | 8 | > 0 | 0 | 5.0000000000000000 > 2659 | 1663 | 1 | 0 | 7 | > 0 | 0 | 5.0000000000000000 > 2659 | 1663 | 229553 | 0 | 7 | > 0 | 0 | 5.0000000000000000 > (3 rows) > > Time: 20.991 ms > > postgres=3D# SELECT * FROM pg_buffercache_relation_stats() WHERE > relfilenode =3D 2659; > relfilenode | reltablespace | reldatabase | relforknumber | buffers | > buffers_dirty | buffers_pinned | usagecount_avg > -------------+---------------+-------------+---------------+---------+---= ------------+----------------+---------------- > 2659 | 1663 | 1 | 0 | 7 | > 0 | 0 | 5 > 2659 | 1663 | 229553 | 0 | 7 | > 0 | 0 | 5 > 2659 | 1663 | 5 | 0 | 8 | > 0 | 0 | 5 > (3 rows) > > Time: 2.912 ms > > With the new function this gets done before putting the data in the > tuplestore used for the set-returning function. Overall, we find that the proposed feature is useful. The proposed way is much cheaper, especially when the number of per-relation stats is not large. Here are review comments on the v1 patch: --- - pg_buffercache--1.5--1.6.sql pg_buffercache--1.6--1.7.sql + pg_buffercache--1.5--1.6.sql pg_buffercache--1.6--1.7.sql \ + pg_buffercache--1.7--1.8.sql Since commit 4b203d499c6 bumped the version from 1.6 to 1.7 last November, we think we don't need to bump the version again for this new feature. --- +/* + * Hash key for pg_buffercache_relation_stats =E2=80=94 groups by relation= identity. + */ +typedef struct +{ + RelFileNumber relfilenumber; + Oid reltablespace; + Oid reldatabase; + ForkNumber forknum; +} BufferRelStatsKey; + +/* + * Hash entry for pg_buffercache_relation_stats =E2=80=94 accumulates per-= relation + * buffer statistics. + */ +typedef struct +{ + BufferRelStatsKey key; /* must be first */ + int32 buffers; + int32 buffers_dirty; + int32 buffers_pinned; + int64 usagecount_total; +} BufferRelStatsEntry; Can we move these typedefs above function prototypes as other typedefs are defined there? --- + relstats_hash =3D hash_create("pg_buffercache relation stats", + 128, + &hash_ctl, + HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); It might be worth considering simplehash.h for even better performance. --- + while ((entry =3D (BufferRelStatsEntry *) hash_seq_search(&hash_seq)) != =3D NULL) + { + if (entry->buffers =3D=3D 0) + continue; + We might want to put CHECK_FOR_INTERRUPTS() here too as the number of entries can be as many as NBuffers in principle. --- We've discussed there might be room for improvement in the function name. For example, pg_buffercache_relations instead of pg_buffercache_relation_stats might be a good name, since everything in this module is stats. if we drop "_stats" then "relation" should be plural, to match other functions in the module ("pages", "os_pages", "numa_pages", "usage_counts"). Regards, --=20 Masahiko Sawada Amazon Web Services: https://aws.amazon.com