From 075168ce72f36b0bf30928dcea86f38aa6f0d369 Mon Sep 17 00:00:00 2001 From: Andrey Borodin Date: Sun, 26 Jul 2026 01:22:57 +0500 Subject: [PATCH v7 1/3] Reuse a zstd compression context for WAL compression XLogCompressBackupBlock() called ZSTD_compress(), which creates and destroys a ZSTD_CCtx on every call. At the default compression level that context is about 1.3MB, so wal_compression = zstd paid for one allocation per full-page image, and a record can carry up to XLR_MAX_BLOCK_ID + 1 of them. Create it on first use and keep it for the life of the backend, compressing with ZSTD_compressCCtx(), which is otherwise equivalent and produces identical output. A statement logging 48k full-page images gets about 12% faster. Author: Andrey Borodin --- src/backend/access/transam/xloginsert.c | 30 +++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/backend/access/transam/xloginsert.c b/src/backend/access/transam/xloginsert.c index f2e10b82b7d..d752eb8a762 100644 --- a/src/backend/access/transam/xloginsert.c +++ b/src/backend/access/transam/xloginsert.c @@ -116,6 +116,20 @@ static uint8 curinsert_flags = 0; static XLogRecData hdr_rdt; static char *hdr_scratch = NULL; +#ifdef USE_ZSTD +/* + * Compression context reused across all block images compressed by this + * backend. zstd keeps its match tables and window in here, roughly 1.3MB at + * the default level, and allocates them on first use. Creating a context per + * call would repeat that allocation for every full-page image. + * + * It is deliberately not freed when wal_compression changes: a backend that + * compressed once is likely to do it again, and the context is only reachable + * from here. + */ +static ZSTD_CCtx *zstd_cctx = NULL; +#endif + #define SizeOfXlogOrigin (sizeof(ReplOriginId) + sizeof(char)) #define SizeOfXLogTransactionId (sizeof(TransactionId) + sizeof(char)) @@ -1064,10 +1078,18 @@ XLogCompressBackupBlock(const PageData *page, uint16 hole_offset, uint16 hole_le case WAL_COMPRESSION_ZSTD: #ifdef USE_ZSTD - len = ZSTD_compress(dest, COMPRESS_BUFSIZE, source, orig_len, - ZSTD_CLEVEL_DEFAULT); - if (ZSTD_isError(len)) - len = -1; /* failure */ + if (zstd_cctx == NULL) + zstd_cctx = ZSTD_createCCtx(); + + if (zstd_cctx == NULL) + len = -1; /* out of memory; store the image as is */ + else + { + len = ZSTD_compressCCtx(zstd_cctx, dest, COMPRESS_BUFSIZE, + source, orig_len, ZSTD_CLEVEL_DEFAULT); + if (ZSTD_isError(len)) + len = -1; /* failure */ + } #else elog(ERROR, "zstd is not supported by this build"); #endif -- 2.50.1 (Apple Git-155)