Skip to content

Enable compression on remote messages - #4137

Merged
nrwahl2 merged 11 commits into
ClusterLabs:mainfrom
clumens:remote-decompress
Aug 5, 2026
Merged

Enable compression on remote messages#4137
nrwahl2 merged 11 commits into
ClusterLabs:mainfrom
clumens:remote-decompress

Conversation

@clumens

@clumens clumens commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Decompression on the receive side has been supported forever in a "just in case we ever want this..." basis. However, the fact that it was unused meant there were bugs in it, for instance the recent integer overflow stuff. So, I'm enabling it here both to ensure it gets used and therefore is less buggy and to continue improving our scalability.

I've tested the following combinations to try to make sure there's no backwards compatibility problems here:

✓ old cluster, new remote CIB, no compression
✓ old cluster, new remote CIB, force compression
✓ old cluster, new remote node, no compression
✓ old cluster, new remote node, force compression
✓ new cluster, old remote CIB, no compression
✓ new cluster, old remote CIB, force compression
✓ new cluster, new remote node, no compression
✓ new cluster, new remote node, force compression
✓ new cluster, old remote node, no compression
✓ new cluster, old remote node, force compression

@clumens
clumens requested a review from nrwahl2 June 24, 2026 18:55

@nrwahl2 nrwahl2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed everything EXCEPT the last commit.

Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c Outdated

free(remote->buffer);
remote->buffer = uncompressed;
header = localized_remote_header(remote);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written, assigning this to header is pointless. header goes out of scope one line later when we return from this function.

We need this function to take a pointer-to-pointer argument, in order for this value to reach the caller. Then use *header in all the places where we use header currently.

I suspect this would break if the endianness differed between the systems.


Currently the header argument could be const. However, it will need to remain non-const when we change it to a pointer-to-pointer.


Edit to add: It works because a later commit moves this assignment back to the caller. At that point, this function can take a const argument and doesn't need a pointer-to-pointer.

We could add a note that this function invalidates the header pointer in the caller. header actually always points to remote->buffer; it exists as a variable for type casting purposes. This function frees and reassigns remote->buffer on success.

Another idea that sounds a bit appealing to me right now (very late at night lol)... if the following is valid syntax, we could have a separate header struct object, instead of pointing at remote->buffer. Like:

struct remote_header_v0 header = { 0, };
...
header = *((struct remote_header_v0 *) remote->buffer);

Just to have a little less coupling after the assignment. I'm not passionate about this though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated this area as follows:

  • Remove the unnecessary header = localized_remote_header(remote); from the patch that introduces handle_compressed_payload
  • Do the assignment after the one caller.
  • Add doxygen on handle_compressed_payload to explain that it invalidates the header parameter.
  • Make the header parameter const.

Doing so allowed me the "Remove a redundant call..." patch, and then since I changed the comments, I also dropped the "Minor reformatting..." patch.

I can make further changes in this area if necessary.

Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c
Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c Outdated
@clumens clumens added the review: in progress PRs that are currently being reviewed label Jun 25, 2026

@nrwahl2 nrwahl2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Minor, annoying comments, most of which don't strictly have to be acted upon.

Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c
@nrwahl2 nrwahl2 added waiting for author Review has been provided, and we're waiting for a response from the author of the pull request and removed review: in progress PRs that are currently being reviewed labels Jun 26, 2026
@clumens
clumens force-pushed the remote-decompress branch from e1a54f5 to 83c7f4a Compare June 29, 2026 13:14
@clumens clumens added review: in progress PRs that are currently being reviewed and removed waiting for author Review has been provided, and we're waiting for a response from the author of the pull request labels Jun 29, 2026
@clumens clumens added the needs attention PRs that someone needs to look at label Jul 27, 2026
@clumens
clumens force-pushed the remote-decompress branch from 83c7f4a to d63bedf Compare August 4, 2026 13:54
@clumens

clumens commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@nrwahl2 Do you have any additional comments on this PR? If not, I'm gonna merge it.

@nrwahl2 nrwahl2 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote most of these comments a month ago, but I never finished and submitted the review. Finished now.

Comment thread lib/common/remote.c
&new_size) == pcmk_rc_ok) {

/* The above call might have given us a new_size value that's
* different from the original payload_len, so we need to repeat the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No action needed:

You'd expect the new_size would be less than or equal to payload_len since we're compressing it. But the libbz2 doc cryptically says "To guarantee that the compressed data will fit in its buffer, allocate an output buffer of size 1% larger than the uncompressed data, plus six hundred extra bytes." Which I guess is somehow a detail of the compression algorithm.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a pretty odd comment. I wish there were more explanation. I wonder if it needs some temporary working space to shuffle things around, or if there's some pathological case where compressed data can end up larger than uncompressed data and that's the maximum size it could ever be.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For posterity:

The "1%" and "six hundred" go back to the beginning of the manual, with no explanation that I can find in the commit messages or anything.
https://sourceware.org/git/?p=bzip2.git;a=commitdiff;h=977101ad5f833f5c0a574bfeea408e5301a6b052

The most recent version of the manual (which wasn't appearing for me in web searches) at least says the following:

Compression is always performed, even if the compressed file is slightly larger than the original. Files of less than about one hundred bytes tend to get larger, since the compression mechanism has a constant overhead in the region of 50 bytes. Random data (including the output of most file compressors) is coded at about 8.05 bits per byte, giving an expansion of around 0.5%.

Ref: https://sourceware.org/bzip2/manual/manual.html

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we touch this code again, it wouldn't hurt to a comment that links to or quotes from the manual, so that we know where the recommendation came from at least.

Comment thread lib/common/remote.c Outdated
Comment thread lib/common/remote.c Outdated
Comment thread lib/common/strings.c Outdated
Comment thread lib/common/strings.c Outdated
Comment thread lib/common/strings.c Outdated
Comment thread lib/common/strings.c
dest_len = max;
rc = BZ2_bzBuffToBuffCompress(compressed, &dest_len, uncompressed, length,
PCMK__BZ2_BLOCKS, 0, PCMK__BZ2_WORK);
*result_len = dest_len;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...We really should never run into the bizarre scenario where UINT_MAX > SIZE_MAX, dest_len > length (i.e., the compressed size is greater than the uncompressed size, which is apparently possible based on the recommended value for max), and this assignment overflows. I'm fine with not worrying about that.

clumens added 11 commits August 5, 2026 10:53
This just splits the code that handles the possibility of a compressed
payload out of pcmk__remote_message_xml and into its own function.  Also
return a standard pacemaker return code.
* Perform this check before even looking at the payload.

* The error message doesn't need to refer to being able to decompress
  the payload.  It doesn't have anything to do with that.

* Remove a redundant check that happens later on during payload XML
  parsing.
Even though it's sent plain text, it might still be compressed and
therefore printing it out will just look like garbage.
The idea here is that we can consolidate some of the integer overflow
checking into this function, making its callers less complicated.

Note that send_cpg_text needs a lot of the same checking that's been
added to pcmk__remote_send_xml recently, though we don't need to be
quite as defensive in that function.  We don't allow just any client to
connect to the cluster layer, so the potential for bad actors should be
considerably lower.

For the moment I'm just making sure the function continues to compile.
Also note that send_cpg_text never returns false indicating that sending
failed.  There's probably a lot of error handling that could be done.
This has been enabled on the receive side of remote connections since
2013 just in case we ever want to use it.  However, because we've never
sent compressed messages, nothing ever used this code and so it's
developed various bugs.  I think it's better if we just enable this code
instead of carrying around unused and buggy code.

There's not really much to do in pcmk__remote_send_xml.  We just need to
make sure that the message is big enough to make compression worth it,
do the compression, and make sure all the sizes are correct in the
message header.  We also need to be careful to free things at the right
time and with the right function given that pcmk__compress allocates
memory for the compressed version.

Two additional notes:

* I've chosen PCMK__BZ2_THRESHOLD as the definition of "big enough"
  because we are using this elsewhere for the same purpose, but we could
  experiment with different values here.  I do not want to make this a
  user-exposed tuneable.

* You may remember that I previously removed compression support for IPC
  messages in favor of multi-part messages.  Adding compression support
  here might seem a little inconsistent, but I'm unsure that we can
  actually do that and still retain backwards compatibility.  At the
  least, we would have to bump the remote message protocol and probably
  change the header format which would make it difficult to support
  older remote systems.  For now at least, using the compression code
  we essentially already supported is easiest.
I don't find this information useful, though it could be handy to see
that compression happened.  Also, use %zu as the format specifier for a
size_t in the error message.
@clumens
clumens force-pushed the remote-decompress branch from d63bedf to cad5f33 Compare August 5, 2026 16:59
@nrwahl2
nrwahl2 merged commit a1965a9 into ClusterLabs:main Aug 5, 2026
1 check passed
@clumens
clumens deleted the remote-decompress branch August 17, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs attention PRs that someone needs to look at review: in progress PRs that are currently being reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants