Re: refactoring basebackup.c
Jeevan Ladhe <jeevan.ladhe@enterprisedb.com>
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Document BaseBackupSync and BaseBackupWrite wait events.
- 749320cdc3fd 15.3 landed
- 4b1ad19a4e22 16.0 landed
-
Support long distance matching for zstd compression
- 2820adf7755d 16.0 landed
-
Fix possible NULL-pointer-deference in backup_compression.c.
- 8e053dc6dfbe 15.0 landed
-
Allow parallel zstd compression when taking a base backup.
- 51c0d186d99a 15.0 landed
-
Make PostgreSQL::Test::Cluster::run_log() return a useful value.
- ad4f2c47de44 15.0 landed
-
Fix a few goofs in new backup compression code.
- 61762426e6ed 15.0 landed
-
Replace BASE_BACKUP COMPRESSION_LEVEL option with COMPRESSION_DETAIL.
- ffd53659c46a 15.0 landed
-
Add 'basebackup_to_shell' contrib module.
- c6306db24bd9 15.0 landed
-
Allow extensions to add new backup targets.
- e4ba69f3f4a1 15.0 landed
-
Change HAVE_LIBLZ4 and HAVE_LIBZSTD tests to USE_LZ4 and USE_ZSTD.
- 75eae090876f 15.0 landed
-
pg_basebackup: Clean up some bogus file extension tests.
- d6f1cdeb9a9e 15.0 landed
-
pg_basebackup: Avoid unclean failure with server-compression and -D -.
- b2de45f9200d 15.0 landed
-
Fix LZ4 tests for remaining buffer space.
- 1d4be6be65ab 15.0 landed
-
Add support for zstd base backup compression.
- 7cf085f077df 15.0 landed
-
pg_basebackup: Allow client-side LZ4 (de)compression.
- 751b8d23b788 15.0 landed
-
Add suport for server-side LZ4 base backup compression.
- dab298471ff2 15.0 landed
-
Add min() and max() aggregates for xid8.
- 400fc6b6487d 15.0 cited
-
Remove superfluous variable.
- 82331ed4dd60 15.0 landed
-
pg_basebackup: Cleaner handling when compression is multiply specified.
- 51891d5a9560 15.0 landed
-
Allow server-side compression to be used with -Fp.
- d45099425eb1 15.0 landed
-
pg_basebackup: Fix a couple of recently-introduced bugs.
- dabf63bc9a5b 15.0 landed
-
Tidy up a few cosmetic issues related to pg_basebackup.
- e1f860f13459 15.0 landed
-
Server-side gzip compression.
- 0ad8032910d5 15.0 landed
-
Unbreak pg_basebackup/t/010_pg_basebackup.pl on msys
- 4f0bcc735038 15.0 cited
-
Suppress variable-set-but-not-used warning from clang 13.
- dc43fc9b3aa3 15.0 cited
-
Extend the options of pg_basebackup to control compression
- 5c649fe15336 15.0 cited
-
Support base backup targets.
- 3500ccc39b0d 15.0 landed
-
Modify pg_basebackup to use a new COPY subprotocol for base backups.
- cc333f32336f 15.0 landed
-
Document that tar archives are now properly terminated.
- 81fca310b38e 15.0 landed
-
Fix thinko in bbsink_throttle_manifest_contents.
- 1b098da20093 15.0 landed
-
Have the server properly terminate tar archives.
- 5a1007a5088c 15.0 landed
-
Minimal fix for unterminated tar archive problem.
- 57b5a9646d97 15.0 landed
-
Introduce 'bbstreamer' abstraction to modularize pg_basebackup.
- 23a1c6578c87 15.0 landed
-
Introduce 'bbsink' abstraction to modularize base backup code.
- bef47ff85df1 15.0 landed
-
Refactor basebackup.c's _tarWriteDir() function.
- 967a17fe2fa7 15.0 landed
-
Flexible options for CREATE_REPLICATION_SLOT.
- 0266e98c6b86 15.0 landed
-
Flexible options for BASE_BACKUP.
- 0ba281cb4bf9 15.0 landed
Thanks, Robert for your response.
On Thu, Sep 9, 2021 at 1:09 AM Robert Haas <robertmhaas@gmail.com> wrote:
> On Wed, Sep 8, 2021 at 2:14 PM Jeevan Ladhe
> <jeevan.ladhe@enterprisedb.com> wrote:
> > To give an example, I put some logging statements, and I can see in the
> log:
> > "
> > bytes remaining in mysink->base.bbs_next->bbs_buffer: 16537
> > input size to be compressed: 512
> > estimated size for compressed buffer by LZ4F_compressBound(): 262667
> > actual compressed size: 16
> > "
>
> That is pretty lame. I don't know why it needs a ~256k buffer to
> produce 16 bytes of output.
>
As I mentioned earlier, I think it has something to do with the lz4
blocksize. Currently, I have chosen it has 256kB, which is 262144 bytes,
and here the LZ4F_compressBound() has returned 262667 for worst-case
accommodation of 512 bytes i.e. 262144(256kB) + 512 + I guess some
book-keeping bytes. If I choose to have blocksize as 64K, then this turns
out to be: 66059 which is 65536(64 kB) + 512 + bookkeeping bytes.
The way the gzip APIs I used work, you tell it how big the output
> buffer is and it writes until it fills that buffer, or until the input
> buffer is empty, whichever happens first. But this seems to be the
> other way around: you tell it how much input you have, and it tells
> you how big a buffer it needs. To handle that elegantly, I think I
> need to make some changes to the design of the bbsink stuff. What I'm
> thinking is that each bbsink somehow tells the next bbsink how big to
> make the buffer. So if the LZ4 buffer is told that its buffer should
> be at least, I don't know, say 64kB. Then it can compute how large an
> output buffer the LZ4 library requires for 64kB. Hopefully we can
> assume that liblz4 never needs a smaller buffer for a larger input.
> Then we can assume that if a 64kB input requires, say, a 300kB output
> buffer, every possible input < 64kB also requires an output buffer <=
> 300 kB.
>
I agree, this assumption is fair enough.
But we can't just say, well, we were asked to create a 64kB buffer (or
> whatever) so let's ask the next bbsink for a 300kB buffer (or
> whatever), because then as soon as we write any data at all into it
> the remaining buffer space might be insufficient for the next chunk.
> So instead what I think we should do is have bbsink_lz4 set the size
> of the next sink's buffer to its own buffer size +
> LZ4F_compressBound(its own buffer size). So in this example if it's
> asked to create a 64kB buffer and LZ4F_compressBound(64kB) = 300kB
> then it asks the next sink to set the buffer size to 364kB. Now, that
> means that there will always be at least 300 kB available in the
> output buffer until we've accumulated a minimum of 64 kB of compressed
> data, and then at that point we can flush.
I think this would be relatively clean and would avoid the need for
> the double copying that the current design forced you to do. What do
> you think?
>
I think this should work.
>
> + /*
> + * If we do not have enough space left in the output buffer for this
> + * chunk to be written, first archive the already written contents.
> + */
> + if (nextChunkLen > mysink->base.bbs_next->bbs_buffer_length -
> mysink->bytes_written ||
> + mysink->bytes_written >= mysink->base.bbs_next->bbs_buffer_length)
> + {
> + bbsink_archive_contents(sink->bbs_next, mysink->bytes_written);
> + mysink->bytes_written = 0;
> + }
>
> I think this is flat-out wrong. It assumes that the compressor will
> never generate more than N bytes of output given N bytes of input,
> which is not true. Not sure there's much point in fixing it now
> because with the changes described above this code will have to change
> anyway, but I think it's just lucky that this has worked for you in
> your testing.
I see your point. But for it to be accurate, I think we need to then
considered the return value of LZ4F_compressBound() to check if that
many bytes are available. But, as explained earlier our output buffer is
already way smaller than that.
>
>
+ /*
> + * LZ4F_compressUpdate() returns the number of bytes written into output
> + * buffer. We need to keep track of how many bytes have been cumulatively
> + * written into the output buffer(bytes_written). But,
> + * LZ4F_compressUpdate() returns 0 in case the data is buffered and not
> + * written to output buffer, set autoFlush to 1 to force the writing to
> the
> + * output buffer.
> + */
> + prefs->autoFlush = 1;
>
> I don't see why this should be necessary. Elsewhere you have code that
> caters to bytes being stuck inside LZ4's buffer, so why do we also
> require this?
>
This is needed to know the actual bytes written in the output buffer. If it
is
set to 0, then LZ4F_compressUpdate() would randomly return 0 or actual
bytes are written to the output buffer, depending on whether it has buffered
or really flushed data to the output buffer.
IIUC, you are referring to the following comment for
bbsink_lz4_end_archive():
"
* There might be some data inside lz4's internal buffers; we need to get
* that flushed out, also finalize the lz4 frame and then get that forwarded
* to the successor sink as archive content.
"
I think it should be modified to:
"
* Finalize the lz4 frame and then get that forwarded to the successor sink
as
* archive content.
"
Regards,
Jeevan Ladhe.