Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nNJRX-0000tZ-GW for pgsql-hackers@arkaria.postgresql.org; Thu, 24 Feb 2022 19:06:23 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1nNJRV-0004Fc-3O for pgsql-hackers@arkaria.postgresql.org; Thu, 24 Feb 2022 19:06:21 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nNJRU-0004F5-L7 for pgsql-hackers@lists.postgresql.org; Thu, 24 Feb 2022 19:06:20 +0000 Received: from mail-oi1-x236.google.com ([2607:f8b0:4864:20::236]) by makus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1nNJRQ-0002if-O3 for pgsql-hackers@postgresql.org; Thu, 24 Feb 2022 19:06:19 +0000 Received: by mail-oi1-x236.google.com with SMTP id q5so4288302oij.6 for ; Thu, 24 Feb 2022 11:06:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=crunchydata.com; s=google; h=user-agent:from:to:subject:date:message-id:mime-version; bh=UQKCnnO72XyDCleQN0Skze+NLQlpbcQrsJyWVVbAPtI=; b=mcc0SgHq+Kvns154Nckt4bMfYM5xjuTObN7KxP9ITFNe8zLmnArEUSNJYETxsLykRS g9kR0xpb0KnMTnTPEO/8eo4DiycwiQCpU2imwkK60xupX4d+hhaT1UsE6Q5A8IKzDyiw O/VAsmO3lwG/PeHPRP17yVD3mbSYIVCjWkkyjPpestdfjtAhpxBE8KwJWl7+/AK0TMf0 q9yD5f1p/pCh4vP5Ym7fLhWu3Tu2EyOnt/DsCLRIFO+A0M3QAaJkTE/zvoO9OaAREpe1 tEec3zYjhqJCgVM82dIA9HqxSsU6fDUsNSs9vQ4FTUhNOaad3V5IbS6zM3GLjGL31p1g Qu7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:user-agent:from:to:subject:date:message-id :mime-version; bh=UQKCnnO72XyDCleQN0Skze+NLQlpbcQrsJyWVVbAPtI=; b=AUsZs4O9ecA7pA2dg/rYHsjFG31Jox/i9azeTHI1/EKMHJDq8mTRtghausfpBHFhO3 oJnyeeKJquGjUu+0pO0Y/Orvh1yHS4MH6bdThvGhqu/zJWvIdK6VnxS3KcZQtvIuPTuH UVBAuF7yiKSU/ym8Sn/Et7qlYO51Evb3gb3dB05ZKmSRwZe4FW6ZcNFgAQVXdyN8OtGW iNjn5buNtG2l/8LShtvABXIzEN5LCtj5MTyWrWnXGiTOn3+CaCVkGeii8EUOzq1ayN+E Ma5dUQ4nJFLUQiWA8mzjFZ55iEC0diYRtF56ND6V2PNH90wi27LKfLAoIEeQh7CMBVR1 Ovsg== X-Gm-Message-State: AOAM5339GifZWpgNu0ciGAaRbpsXhag0YATSjV7/HbXumOPXc093D05B YrlHNzOVFkGKE5ajJXs0W46BdQnDKNV/KltT X-Google-Smtp-Source: ABdhPJx0RPgG8Cenof0qNO+YKpyx3LHwECBO5V4bQgZDJNRqgGQxsLGVEnHu3P6B33tQ36Jx9vVWyA== X-Received: by 2002:a05:6870:832a:b0:d6:de7e:60f5 with SMTP id p42-20020a056870832a00b000d6de7e60f5mr663084oae.290.1645729575561; Thu, 24 Feb 2022 11:06:15 -0800 (PST) Received: from veeddrois.attlocal.net ([2600:1700:5ac0:aa80:10f2:57af:1768:5832]) by smtp.gmail.com with ESMTPSA id p22-20020a056870831600b000ccfbea4f23sm273040oae.33.2022.02.24.11.06.14 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Thu, 24 Feb 2022 11:06:15 -0800 (PST) User-agent: mu4e 1.6.2; emacs 27.1 From: David Christensen To: pgsql-hackers@postgresql.org Subject: [PATCH] add relation and block-level filtering to pg_waldump Date: Thu, 24 Feb 2022 13:02:33 -0600 Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --=-=-= Content-Type: text/plain Greetings, This patch adds the ability to specify a RelFileNode and optional BlockNum to limit output of pg_waldump records to only those which match the given criteria. This should be more performant than `pg_waldump | grep` as well as more reliable given specific variations in output style depending on how the blocks are specified. This currently affects only the main fork, but we could presumably add the option to filter by fork as well, if that is considered useful. Best, David --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-Add-relation-block-filtering-to-pg_waldump.patch Content-Description: pg_waldump-filter-relation-block.patch From 9194b2cb07172e636030b9b4e979b7f2caf7cbc0 Mon Sep 17 00:00:00 2001 From: David Christensen Date: Thu, 24 Feb 2022 11:00:46 -0600 Subject: [PATCH] Add relation/block filtering to pg_waldump This feature allows you to only output records that are targeting a specific RelFileNode and optional BlockNumber within this relation. Currently only applies this filter to the relation's main fork. --- doc/src/sgml/ref/pg_waldump.sgml | 23 ++++++++++ src/bin/pg_waldump/pg_waldump.c | 74 +++++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/ref/pg_waldump.sgml b/doc/src/sgml/ref/pg_waldump.sgml index 5735a161ce..c953703bc8 100644 --- a/doc/src/sgml/ref/pg_waldump.sgml +++ b/doc/src/sgml/ref/pg_waldump.sgml @@ -100,6 +100,29 @@ PostgreSQL documentation + + + + + + Display only records touching the given block. (Requires also + providing the relation via .) + + + + + + + + + + Display only records touching the given relation. The relation is + specified via tablespace oid, database oid, and relfilenode separated + by slashes. + + + + diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c index a6251e1a96..faae547a5c 100644 --- a/src/bin/pg_waldump/pg_waldump.c +++ b/src/bin/pg_waldump/pg_waldump.c @@ -55,6 +55,10 @@ typedef struct XLogDumpConfig bool filter_by_rmgr_enabled; TransactionId filter_by_xid; bool filter_by_xid_enabled; + RelFileNode filter_by_relation; + bool filter_by_relation_enabled; + BlockNumber filter_by_relation_block; + bool filter_by_relation_block_enabled; } XLogDumpConfig; typedef struct Stats @@ -394,6 +398,34 @@ WALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, return count; } +/* + * Boolean to return whether the given WAL record matches a specific relation and optional block + */ +static bool +XLogRecordMatchesRelationBlock(XLogReaderState *record, RelFileNode matchRnode, BlockNumber matchBlock) +{ + RelFileNode rnode; + ForkNumber forknum; + BlockNumber blk; + int block_id; + + for (block_id = 0; block_id <= record->max_block_id; block_id++) + { + if (!XLogRecHasBlockRef(record, block_id)) + continue; + + XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk); + + if (forknum == MAIN_FORKNUM && + matchRnode.spcNode == rnode.spcNode && + matchRnode.dbNode == rnode.dbNode && + matchRnode.relNode == rnode.relNode && + (matchBlock == InvalidBlockNumber || matchBlock == blk)) + return true; + } + return false; +} + /* * Calculate the size of a record, split into !FPI and FPI parts. */ @@ -767,6 +799,8 @@ usage(void) printf(_(" -b, --bkp-details output detailed information about backup blocks\n")); printf(_(" -e, --end=RECPTR stop reading at WAL location RECPTR\n")); printf(_(" -f, --follow keep retrying after reaching end of WAL\n")); + printf(_(" -k, --block=N only show records matching a given relation block (requires -l)\n")); + printf(_(" -l, --relation=N/N/N only show records that touch a specific relation\n")); printf(_(" -n, --limit=N number of records to display\n")); printf(_(" -p, --path=PATH directory in which to find log segment files or a\n" " directory with a ./pg_wal that contains such files\n" @@ -802,12 +836,14 @@ main(int argc, char **argv) static struct option long_options[] = { {"bkp-details", no_argument, NULL, 'b'}, + {"block", required_argument, NULL, 'k'}, {"end", required_argument, NULL, 'e'}, {"follow", no_argument, NULL, 'f'}, {"help", no_argument, NULL, '?'}, {"limit", required_argument, NULL, 'n'}, {"path", required_argument, NULL, 'p'}, {"quiet", no_argument, NULL, 'q'}, + {"relation", required_argument, NULL, 'l'}, {"rmgr", required_argument, NULL, 'r'}, {"start", required_argument, NULL, 's'}, {"timeline", required_argument, NULL, 't'}, @@ -860,6 +896,8 @@ main(int argc, char **argv) config.filter_by_rmgr_enabled = false; config.filter_by_xid = InvalidTransactionId; config.filter_by_xid_enabled = false; + config.filter_by_relation_enabled = false; + config.filter_by_relation_block_enabled = false; config.stats = false; config.stats_per_record = false; @@ -872,7 +910,7 @@ main(int argc, char **argv) goto bad_argument; } - while ((option = getopt_long(argc, argv, "be:fn:p:qr:s:t:x:z", + while ((option = getopt_long(argc, argv, "be:fk:l:n:p:qr:s:t:x:z", long_options, &optindex)) != -1) { switch (option) @@ -892,6 +930,25 @@ main(int argc, char **argv) case 'f': config.follow = true; break; + case 'k': + if (sscanf(optarg, "%ul", &config.filter_by_relation_block) != 1) + { + pg_log_error("could not parse block number \"%s\"", optarg); + goto bad_argument; + } + config.filter_by_relation_block_enabled = true; + break; + case 'l': + if (sscanf(optarg, "%d/%d/%d", + &config.filter_by_relation.spcNode, + &config.filter_by_relation.dbNode, + &config.filter_by_relation.relNode) != 3) + { + pg_log_error("could not parse relation from \"%s\" (expecting \"spc/dat/rel\")", optarg); + goto bad_argument; + } + config.filter_by_relation_enabled = true; + break; case 'n': if (sscanf(optarg, "%d", &config.stop_after_records) != 1) { @@ -978,6 +1035,12 @@ main(int argc, char **argv) } } + if (config.filter_by_relation_block_enabled && !config.filter_by_relation_enabled) + { + pg_log_error("cannot filter by --block without also filtering --relation"); + goto bad_argument; + } + if ((optind + 2) < argc) { pg_log_error("too many command-line arguments (first is \"%s\")", @@ -1150,6 +1213,15 @@ main(int argc, char **argv) config.filter_by_xid != record->xl_xid) continue; + /* check for extended filtering */ + if (config.filter_by_relation_enabled && + !XLogRecordMatchesRelationBlock( + xlogreader_state, + config.filter_by_relation, + config.filter_by_relation_block_enabled ? config.filter_by_relation_block : InvalidBlockNumber + )) + continue; + /* perform any per-record work */ if (!config.quiet) { -- 2.32.0 (Apple Git-132) --=-=-= Content-Type: text/plain -- --=-=-=--