Thread
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
doc: Fix pg_buffercache_evict() title
- 59d6c0395619 18.0 cited
-
Fix several hash functions that were taking chintzy shortcuts instead of
- 1f559b7d3aa4 8.3.0 cited
-
hash_search_with_hash_value is high in "perf top" on a replica
Dmitry Koterov <dmitry.koterov@gmail.com> — 2025-01-31T11:30:35Z
Hi. Debugging some replication lag on a replica when the master node experiences heavy writes. PG "startup recovering" eats up a lot of CPU (like 65 %user and 30 %sys), which is a little surprising (what is it doing with all those CPU cycles? it looked like WAL replay should be more IO bound than CPU bound?). Running "perf top -p <pid>", it shows this: Samples: 1M of event 'cycles:P', 4000 Hz, Event count (approx.): 18178814660 lost: 0/0 drop: 0/0 Overhead Shared Object Symbol 16.63% postgres [.] hash_search_with_hash_value 5.38% postgres [.] __aarch64_ldset4_sync 4.42% postgres [.] __aarch64_cas4_acq_rel 3.42% postgres [.] XLogReadBufferExtended 2.35% libc.so.6 [.] 0x0000000000097f4c 2.04% postgres [.] pg_comp_crc32c_armv8 1.77% [kernel] [k] mutex_lock 1.63% postgres [.] XLogPrefetcherReadRecord 1.56% postgres [.] DecodeXLogRecord 1.55% postgres [.] LWLockAcquire This is more surprising: hash_search_with_hash_value is a hash table lookup function, is it expected that it is #1 in the profiler? (Called mostly from PrefetchSharedBuffer*.) Configuration: - PG17 - ZFS, compression is off, empty database (with compression on, most of "startup recovering" CPU was spent in ZFS guts doing compression/decompression according to the profiler) - full_page_writes=off, recovery_prefetch=on (ZFS supports it, I tested with a small C program), wal_decode_buffer_size=2048kB (it doesn't seem to affect anything though). - shared_buffers = 25% of RAM - testing with a giant COPY command basically My main question is about hash_search_with_hash_value <https://github.com/postgres/postgres/blob/59d6c03956193f622c069a4ab985bade27384ac4/src/backend/utils/hash/dynahash.c#L968> CPU usage. With both recovery_prefetch=on and off, it is the topmost function in "perf top". I see no IO bottleneck on the machine, it's only "startup recovering" maxing out one CPU core. Maybe it's a red herring though, but it looks pretty suspicious.
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Srinath Reddy Sadipiralla <srinath2133@gmail.com> — 2025-01-31T12:24:09Z
On Fri, Jan 31, 2025 at 5:00 PM Dmitry Koterov <dmitry.koterov@gmail.com> wrote: > Hi. > > Debugging some replication lag on a replica when the master node > experiences heavy writes. > > PG "startup recovering" eats up a lot of CPU (like 65 %user and 30 %sys), > which is a little surprising (what is it doing with all those CPU cycles? > it looked like WAL replay should be more IO bound than CPU bound?). > > Running "perf top -p <pid>", it shows this: > > Samples: 1M of event 'cycles:P', 4000 Hz, Event count (approx.): > 18178814660 lost: 0/0 drop: 0/0 > Overhead Shared Object Symbol > 16.63% postgres [.] hash_search_with_hash_value > 5.38% postgres [.] __aarch64_ldset4_sync > 4.42% postgres [.] __aarch64_cas4_acq_rel > 3.42% postgres [.] XLogReadBufferExtended > 2.35% libc.so.6 [.] 0x0000000000097f4c > 2.04% postgres [.] pg_comp_crc32c_armv8 > 1.77% [kernel] [k] mutex_lock > 1.63% postgres [.] XLogPrefetcherReadRecord > 1.56% postgres [.] DecodeXLogRecord > 1.55% postgres [.] LWLockAcquire > > This is more surprising: hash_search_with_hash_value is a hash table > lookup function, is it expected that it is #1 in the profiler? (Called > mostly from PrefetchSharedBuffer*.) > > Configuration: > - PG17 > - ZFS, compression is off, empty database (with compression on, most of > "startup recovering" CPU was spent in ZFS guts doing > compression/decompression according to the profiler) > - full_page_writes=off, recovery_prefetch=on (ZFS supports it, I tested > with a small C program), wal_decode_buffer_size=2048kB (it doesn't seem to > affect anything though). > - shared_buffers = 25% of RAM > - testing with a giant COPY command basically > > My main question is about hash_search_with_hash_value > <https://github.com/postgres/postgres/blob/59d6c03956193f622c069a4ab985bade27384ac4/src/backend/utils/hash/dynahash.c#L968> > CPU usage. With both recovery_prefetch=on and off, it is the topmost > function in "perf top". I see no IO bottleneck on the machine, it's only > "startup recovering" maxing out one CPU core. > > Maybe it's a red herring though, but it looks pretty suspicious. > > Hi, i think high CPU usage make sense,as hash_search_with_hash_value is called every time when ever startup process in replica reads a wal and tries to redo it ,as it goes through readbuffer_common to check if the page which the wal effects and wants to redo is in buffer pool or not using BufferAlloc->BufTableLookup>hash_search_with_hash_value. Regards, Srinath Reddy Sadipiralla, EDB: https://www.enterprisedb.com <http://www.enterprisedb.com/>
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Alvaro Herrera <alvherre@alvh.no-ip.org> — 2025-01-31T14:25:18Z
On 2025-Jan-31, Dmitry Koterov wrote: > PG "startup recovering" eats up a lot of CPU (like 65 %user and 30 %sys), > which is a little surprising (what is it doing with all those CPU cycles? > it looked like WAL replay should be more IO bound than CPU bound?). > > Running "perf top -p <pid>", it shows this: > > Samples: 1M of event 'cycles:P', 4000 Hz, Event count (approx.): > 18178814660 lost: 0/0 drop: 0/0 > Overhead Shared Object Symbol > 16.63% postgres [.] hash_search_with_hash_value Yeah, I noticed that this function was showing high in some profiles a couple of days ago as well. Looking now at hashfn.c (hash_bytes_uint32 there is the function involved in the buffer mapping hash table), the comments state that we're updated to Bob Jenkins code from 2006, but there's a version in his website that (if I read correctly) is twice as fast as what we're using now: http://burtleburtle.net/bob/hash/spooky.html Apparently this code in our repo is mostly unchanged since commit 1f559b7d3aa4, in 2007. He mentions that on Intel chips, Google's CityHash is faster; but they in turn claim that the difference is small on Intel chips and that Jenkins' hash is better on AMD chips. https://github.com/google/cityhash Anyway if you wanted to try your luck at improving things, here's your chance. -- Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Yura Sokolov <y.sokolov@postgrespro.ru> — 2025-01-31T14:38:10Z
31.01.2025 17:25, Álvaro Herrera пишет: > On 2025-Jan-31, Dmitry Koterov wrote: > >> PG "startup recovering" eats up a lot of CPU (like 65 %user and 30 %sys), >> which is a little surprising (what is it doing with all those CPU cycles? >> it looked like WAL replay should be more IO bound than CPU bound?). >> >> Running "perf top -p <pid>", it shows this: >> >> Samples: 1M of event 'cycles:P', 4000 Hz, Event count (approx.): >> 18178814660 lost: 0/0 drop: 0/0 >> Overhead Shared Object Symbol >> 16.63% postgres [.] hash_search_with_hash_value > > Yeah, I noticed that this function was showing high in some profiles a > couple of days ago as well. Looking now at hashfn.c (hash_bytes_uint32 > there is the function involved in the buffer mapping hash table), the > comments state that we're updated to Bob Jenkins code from 2006, but > there's a version in his website that (if I read correctly) is twice as > fast as what we're using now: > http://burtleburtle.net/bob/hash/spooky.html > > Apparently this code in our repo is mostly unchanged since commit > 1f559b7d3aa4, in 2007. > > He mentions that on Intel chips, Google's CityHash is faster; but they > in turn claim that the difference is small on Intel chips and that > Jenkins' hash is better on AMD chips. > https://github.com/google/cityhash > > Anyway if you wanted to try your luck at improving things, here's your > chance. > `hash_search_with_hash_value` uses already calculated hash value, so its performance doesn't depend on performance of hash function (hash_bytes_uint32 or any other). I believe, it is memory bound, since dynahash does at least three indirection jumps before it event reaches first node in linked list segp = HTAB->dir[segment_num]; *bucketptr = segp[segment_ndx]; And then iterates through linked list with each iteration is being (for large hash table) both cache and TLB (without huge pages) miss.
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Andres Freund <andres@anarazel.de> — 2025-01-31T14:43:34Z
Hi, On 2025-01-31 03:30:35 -0800, Dmitry Koterov wrote: > Debugging some replication lag on a replica when the master node > experiences heavy writes. > > PG "startup recovering" eats up a lot of CPU (like 65 %user and 30 %sys), > which is a little surprising (what is it doing with all those CPU cycles? > it looked like WAL replay should be more IO bound than CPU bound?). > > Running "perf top -p <pid>", it shows this: > > Samples: 1M of event 'cycles:P', 4000 Hz, Event count (approx.): > 18178814660 lost: 0/0 drop: 0/0 > Overhead Shared Object Symbol > 16.63% postgres [.] hash_search_with_hash_value It'd be interesting to see what the paths towards hash_search_with_hash_value are. You said it's a COPY workloads, which surprises me a bit, because that should normally be a bit less sensitive to it. Perhaps you have triggers or such that prevent use of the multi-insert path? > 5.38% postgres [.] __aarch64_ldset4_sync > 4.42% postgres [.] __aarch64_cas4_acq_rel These two suggest that it might be worth compiling with an -march CPU that provides native atomics (everything above armv8.1-a, I think). > Maybe it's a red herring though, but it looks pretty suspicious. It's unfortunately not too surprising - our buffer mapping table is a pretty big bottleneck. Both because a hash table is just not a good fit for the buffer mapping table due to the lack of locality and because dynahash is really poor hash table implementation. Greetings, Andres Freund
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Dmitry Koterov <dmitry.koterov@gmail.com> — 2025-02-01T11:46:33Z
> It'd be interesting to see what the paths towards hash_search_with_hash_value are. One of the popular paths is on the screenshot. They are all more or less the same when recovery_prefetch=on (and when it's off, the replica behaves worse, more replication lag). That COPY command - it's the initial sync stage after CREATE SUBSCRIPTION basically (with streaming=off, default). Large table (hundreds of gigabytes). [image: image.png] It's also interesting, how gradually the disk writes and IOPS grow on the replica during that large table COPY (dark blue line on the screenshot below). And in the end, when "EXPLAIN SELECT 1 FROM mytable" showed ~100% of the rows (i.e. almost everything is copied), it got stuck or ~5 minutes with a spike on both reads and writes (the dark blue charts are all about the replica). I.e. that COPY load, it is not spread uniformly: in the end, it causes more load (I also saw that on the "startup recovering" process CPU metrics). [image: CleanShot 2025-02-01 at 03.42.16@2x.png] On Fri, Jan 31, 2025 at 6:43 AM Andres Freund <andres@anarazel.de> wrote: > Hi, > > On 2025-01-31 03:30:35 -0800, Dmitry Koterov wrote: > > Debugging some replication lag on a replica when the master node > > experiences heavy writes. > > > > PG "startup recovering" eats up a lot of CPU (like 65 %user and 30 %sys), > > which is a little surprising (what is it doing with all those CPU cycles? > > it looked like WAL replay should be more IO bound than CPU bound?). > > > > Running "perf top -p <pid>", it shows this: > > > > Samples: 1M of event 'cycles:P', 4000 Hz, Event count (approx.): > > 18178814660 lost: 0/0 drop: 0/0 > > Overhead Shared Object Symbol > > 16.63% postgres [.] hash_search_with_hash_value > > It'd be interesting to see what the paths towards > hash_search_with_hash_value > are. > > You said it's a COPY workloads, which surprises me a bit, because that > should > normally be a bit less sensitive to it. Perhaps you have triggers or such > that > prevent use of the multi-insert path? > > > > > 5.38% postgres [.] __aarch64_ldset4_sync > > 4.42% postgres [.] __aarch64_cas4_acq_rel > > These two suggest that it might be worth compiling with an -march CPU that > provides native atomics (everything above armv8.1-a, I think). > > > > Maybe it's a red herring though, but it looks pretty suspicious. > > It's unfortunately not too surprising - our buffer mapping table is a > pretty > big bottleneck. Both because a hash table is just not a good fit for the > buffer mapping table due to the lack of locality and because dynahash is > really poor hash table implementation. > > > > Greetings, > > Andres Freund >
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Ants Aasma <ants.aasma@cybertec.at> — 2025-02-01T14:43:41Z
On Fri, Jan 31, 2025, 15:43 Andres Freund <andres@anarazel.de> wrote: > > Maybe it's a red herring though, but it looks pretty suspicious. > > It's unfortunately not too surprising - our buffer mapping table is a > pretty > big bottleneck. Both because a hash table is just not a good fit for the > buffer mapping table due to the lack of locality and because dynahash is > really poor hash table implementation. > I measured similar things when looking at apply throughput recently. For in-cache workloads buffer lookup and locking was about half of the load. One other direction is to extract more memory concurrency. Prefetcher could batch multiple lookups together so CPU OoO execution has a chance to fire off multiple memory accesses at the same time. The other direction is to split off WAL decoding, buffer lookup and maybe even pinning to a separate process from the main redo loop. -- Ants Aasma >
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Andres Freund <andres@anarazel.de> — 2025-02-01T15:50:37Z
Hi, On 2025-02-01 03:46:33 -0800, Dmitry Koterov wrote: > > It'd be interesting to see what the paths towards > hash_search_with_hash_value > are. > > One of the popular paths is on the screenshot. They are all more or less > the same when recovery_prefetch=on (and when it's off, the replica behaves > worse, more replication lag). Yea, I was hoping for a profile with recovery_prefetch=off, so we can see what records are causing most of the "slow" lookups. Greetings, Andres Freund
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Andres Freund <andres@anarazel.de> — 2025-02-01T15:55:38Z
Hi, On 2025-02-01 15:43:41 +0100, Ants Aasma wrote: > On Fri, Jan 31, 2025, 15:43 Andres Freund <andres@anarazel.de> wrote: > > > > Maybe it's a red herring though, but it looks pretty suspicious. > > > > It's unfortunately not too surprising - our buffer mapping table is a > > pretty > > big bottleneck. Both because a hash table is just not a good fit for the > > buffer mapping table due to the lack of locality and because dynahash is > > really poor hash table implementation. > > > > I measured similar things when looking at apply throughput recently. For > in-cache workloads buffer lookup and locking was about half of the load. > > One other direction is to extract more memory concurrency. Prefetcher could > batch multiple lookups together so CPU OoO execution has a chance to fire > off multiple memory accesses at the same time. I think at the moment we have a *hilariously* cache-inefficient buffer lookup, that's the first thing to address. A hash table for buffer mapping lookups imo is a bad idea, due to loosing all locality in a workload that exhibits a *lot* of locality. But furthermore, dynahash.c is very far from a cache efficient hashtable implementation. The other aspect is that in many workloads we'll look up a small set of buffers over and over, which a) wastes cycles b) wastes cache space for stuff that could be elided much more efficiently. We also do a lot of hash lookups for smgr, because we don't have any cross-record caching infrastructure for that. > The other direction is to split off WAL decoding, buffer lookup and maybe > even pinning to a separate process from the main redo loop. Maybe, but I think we're rather far away from those things being the most productive thing to tackle. Greetings, Andres Freund
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Matthias van de Meent <boekewurm+postgres@gmail.com> — 2025-02-04T19:03:10Z
On Sat, 1 Feb 2025 at 16:55, Andres Freund <andres@anarazel.de> wrote: > > Hi, > > On 2025-02-01 15:43:41 +0100, Ants Aasma wrote: > > On Fri, Jan 31, 2025, 15:43 Andres Freund <andres@anarazel.de> wrote: > > > > > > Maybe it's a red herring though, but it looks pretty suspicious. > > > > > > It's unfortunately not too surprising - our buffer mapping table is a > > > pretty > > > big bottleneck. Both because a hash table is just not a good fit for the > > > buffer mapping table due to the lack of locality and because dynahash is > > > really poor hash table implementation. > > > > > > > I measured similar things when looking at apply throughput recently. For > > in-cache workloads buffer lookup and locking was about half of the load. > > > > One other direction is to extract more memory concurrency. Prefetcher could > > batch multiple lookups together so CPU OoO execution has a chance to fire > > off multiple memory accesses at the same time. > > I think at the moment we have a *hilariously* cache-inefficient buffer lookup, > that's the first thing to address. A hash table for buffer mapping lookups imo > is a bad idea, due to loosing all locality in a workload that exhibits a *lot* > of locality. But furthermore, dynahash.c is very far from a cache efficient > hashtable implementation. In case you might be interested, I've sent a new patch with a new approach to reducing the buffer lookup table's memory in [0], which attempts to create a more cache-efficient hash table implementation. Kind regards, Matthias van de Meent Neon (https://neon.tech) [0] https://www.postgresql.org/message-id/CAEze2WiRo4Zu71jwxYmqjq6XK814Avf2-kytaL6n%3DBreZR2ZbA%40mail.gmail.com
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Thomas Munro <thomas.munro@gmail.com> — 2025-02-04T21:22:06Z
On Sun, Feb 2, 2025 at 3:44 AM Ants Aasma <ants.aasma@cybertec.at> wrote: > The other direction is to split off WAL decoding, buffer lookup and maybe even pinning to a separate process from the main redo loop. Hi Ants, FWIW I have a patch set that changes xlogprefetcher.c to use read_stream.c, which I hope to propose for 19. It pins ahead of time, which means that it already consolidates some pin/unpin/repin sequences when it looks ahead, a little bit like what you're suggesting, just not in a separate process. Here[1] is a preview, but it needs some major rebasing and has some issues... it's on my list... I mention this because, although the primary goal of the recovery-streamification project is recovery I/O performance, especially anticipating direct I/O (especially without FPW), there are many more opportunities along those lines for cached blocks. I've written about some of them before and posted toy sketch code, but I didn't let myself get too distracted as the first thing seems to be real streamification (replaying LsnReadQueue). With a bit more work it could avoid lots of buffer mapping table lookups too: I posted a few ideas and toy patches, one based on already held pins which begins to pay off once you hold many pins in a window, and one more generally caching recently accessed buffers in smgr relations. That'd be independent of whether our buffer mapping table also sucks and needs a rewrite, it bypasses it completely in many interesting cases, and would need to be so cheap as to be basically free when it doesn't help. An assumption I just made, in remembering all that: OP didn't mention it but I guess that this COPY replay is probably repeatedly hammering the same pages from separate records here, because otherwise multi-insert stuff would already avoid a lot of mapping table lookups already? There are many more automatic batching opportunities, like holding content locks across records (I posted a toy patch to measure the potential speedup for that once, too lazy to search for it...), and then to really benefit from all of these things in more general real workloads I think you need to be able to do a little bit of re-ordering because eg heap/index accesses are so often interleaved. IOW there seem to be plenty of lower-hanging fruit in the "mechanical sympathy" category, before you need another thread. No argument against also making the hashing faster, the mapping table better, and eventually parallelising some bits when it's not just compensating for inefficient code that throws away all the locality, I just wanted to mention that related work in the pipeline :-) [1] https://github.com/macdice/postgres/commit/52533b652e544464add6f174019161889a37ed93
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Thomas Munro <thomas.munro@gmail.com> — 2025-02-04T21:25:35Z
On Wed, Feb 5, 2025 at 10:22 AM Thomas Munro <thomas.munro@gmail.com> wrote: > (replaying LsnReadQueue) s/replaying/replacing/
-
Re: hash_search_with_hash_value is high in "perf top" on a replica
Jakub Wartak <jakub.wartak@enterprisedb.com> — 2025-02-10T08:45:05Z
Hi Thomas! On Tue, Feb 4, 2025 at 10:22 PM Thomas Munro <thomas.munro@gmail.com> wrote: > > On Sun, Feb 2, 2025 at 3:44 AM Ants Aasma <ants.aasma@cybertec.at> wrote: > > The other direction is to split off WAL decoding, buffer lookup and maybe even pinning to a separate process from the main redo loop. > > Hi Ants, > [..] > An assumption I just made, in remembering all that: OP didn't > mention it but I guess that this COPY replay is probably repeatedly > hammering the same pages from separate records here, because otherwise > multi-insert stuff would already avoid a lot of mapping table lookups > already? Basic COPY (he mentioned basic CREATE SUBSCRIPTION table copy, so I assume it's fresh): ends emitting stuff like this (note the "blk" is increasing): rmgr: Heap2 len (rec/tot): 3666/ 3666, tx: 759, lsn: 0/040069A0, prev 0/04004950, desc: MULTI_INSERT+INIT ntuples: 226, flags: 0x00, blkref #0: rel 1663/5/16393 blk 5 rmgr: Heap2 len (rec/tot): 3666/ 3666, tx: 759, lsn: 0/040077F8, prev 0/040069A0, desc: MULTI_INSERT+INIT ntuples: 226, flags: 0x00, blkref #0: rel 1663/5/16393 blk 6 rmgr: Heap2 len (rec/tot): 3666/ 3666, tx: 759, lsn: 0/04008668, prev 0/040077F8, desc: MULTI_INSERT+INIT ntuples: 226, flags: 0x00, blkref #0: rel 1663/5/16393 blk 7 rmgr: Heap2 len (rec/tot): 3122/ 3122, tx: 759, lsn: 0/040094C0, prev 0/04008668, desc: MULTI_INSERT+INIT ntuples: 192, flags: 0x02, blkref #0: rel 1663/5/16393 blk 8 now if the table would have PK , we end up doing *massive* INSERT_LEAFs due to lack of batched btree emision like just in heap2 case: rmgr: Heap2 len (rec/tot): 3666/ 3666, tx: 763, lsn: 0/05000028, prev 0/041C8198, desc: MULTI_INSERT+INIT ntuples: 226, flags: 0x00, blkref #0: rel 1663/5/16396 blk 0 rmgr: Heap2 len (rec/tot): 3666/ 3666, tx: 763, lsn: 0/05000E80, prev 0/05000028, desc: MULTI_INSERT+INIT ntuples: 226, flags: 0x00, blkref #0: rel 1663/5/16396 blk 1 [..] rmgr: Btree len (rec/tot): 64/ 64, tx: 763, lsn: 0/05004050, prev 0/05003FD8, desc: INSERT_LEAF off: 1, blkref #0: rel 1663/5/16398 blk 1 rmgr: Btree len (rec/tot): 64/ 64, tx: 763, lsn: 0/05004090, prev 0/05004050, desc: INSERT_LEAF off: 2, blkref #0: rel 1663/5/16398 blk 1 rmgr: Btree len (rec/tot): 64/ 64, tx: 763, lsn: 0/050040D0, prev 0/05004090, desc: INSERT_LEAF off: 3, blkref #0: rel 1663/5/16398 blk 1 rmgr: Btree len (rec/tot): 64/ 64, tx: 763, lsn: 0/05004110, prev 0/050040D0, desc: INSERT_LEAF off: 4, blkref #0: rel 1663/5/16398 blk 1 rmgr: Btree len (rec/tot): 64/ 64, tx: 763, lsn: 0/05004150, prev 0/05004110, desc: INSERT_LEAF off: 5, blkref #0: rel 1663/5/16398 blk 1 rmgr: Btree len (rec/tot): 64/ 64, tx: 763, lsn: 0/05004190, prev 0/05004150, desc: INSERT_LEAF off: 6, blkref #0: rel 1663/5/16398 blk 1 rmgr: Btree len (rec/tot): 64/ 64, tx: 763, lsn: 0/050041D0, prev 0/05004190, desc: INSERT_LEAF off: 7, blkref #0: rel 1663/5/16398 blk 1 rmgr: Btree len (rec/tot): 64/ 64, tx: 763, lsn: 0/05004210, prev 0/050041D0, desc: INSERT_LEAF off: 8, blkref #0: rel 1663/5/16398 blk 1 rmgr: Btree len (rec/tot): 64/ 64, tx: 763, lsn: 0/05004250, prev 0/05004210, desc: INSERT_LEAF off: 9, blkref #0: rel 1663/5/16398 blk 1 [..] I don't know exactly which situation Dmitry has hit while the second scenario would be much easier to optimize. This of course reminds me of Your earlier work on recet_buffer optimization too and Andres mentioned some form of LRU cache to just protect the hash table for buffer mapping lookups. Also I was under the impression that work by Bhrath [0] could help here too to lower the number of WAL records emitted. @Dmitry : So if you're reading then this is a known problem for a while (max performance ceiling, see [1]), but there are a myriad of possible long-term solutions in the code. Outside of hacking PG changes, you could only probably split the big table on publisher into many smaller partitions (but not too many), and then having COPY running on those smaller ones with some delays so that replication lag doesn't grow too much. The worst alternative is trying to get the fastest possible cores (it's a single threaded bottleneck). As Andres noted you probably could try to recompile with some better -march flag on that ARM and see how much that helps. -J. [0] - https://www.postgresql.org/message-id/flat/CALj2ACVi9eTRYR=gdca5wxtj3Kk_9q9qVccxsS1hngTGOCjPwQ@mail.gmail.com [1] - https://www.postgresql.org/message-id/VI1PR0701MB69608CBCE44D80857E59572EF6CA0%40VI1PR0701MB6960.eurprd07.prod.outlook.com