ZMQ: add options to configure outbound message high water mark, aka SNDHWM
This is my attempt at https://github.com/bitcoin/bitcoin/pull/13315
ZMQ: add options to configure outbound message high water mark, aka SNDHWM
This is my attempt at https://github.com/bitcoin/bitcoin/pull/13315
442 | @@ -443,11 +443,19 @@ void SetupServerArgs() 443 | gArgs.AddArg("-zmqpubhashtx=<address>", "Enable publish hash transaction in <address>", false, OptionsCategory::ZMQ); 444 | gArgs.AddArg("-zmqpubrawblock=<address>", "Enable publish raw block in <address>", false, OptionsCategory::ZMQ); 445 | gArgs.AddArg("-zmqpubrawtx=<address>", "Enable publish raw transaction in <address>", false, OptionsCategory::ZMQ); 446 | + gArgs.AddArg("-zmqpubhashblockhwm=<n>", strprintf("Set publish hash block outbound message high water mark (default: %d)", DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ); 447 | + gArgs.AddArg("-zmqpubhashtxhwm=<n>", strprintf("Set publish hash transaction outbound message high water mark (default: %d)", DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ); 448 | + gArgs.AddArg("-zmqpubrawblockhwm=<n>", strprintf("Set publish raw block outbound message high water mark (default: %d)", DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ); 449 | + gArgs.AddArg("-zmqpubrawtxhwm=<n>", strprintf("Set publish raw transaction outbound message high water mark (default: %d)", DEFAULT_ZMQ_SNDHWM), false, OptionsCategory::ZMQ);
I don't really like the duplication here (one argument for each publisher cross (address, hwm)), but I think it is probably the easiest solution. At least I don't see a good alternative that would not be over-engineered if we want the HWM configurable for each publisher separately.
yeah, i figured that at least it fit a pattern and was easy to code and review. unless someone has a better idea, i think i'll leave it simple.
Maybe one flag to set them all would be interesting?
that was how it was originally done in the other PR and then someone wanted it broken out i believe. it makes sense to me to have them separate because the tx publishes definitely need way more message capacity than the block pubs.
Perhaps there could be just two (not four) HWMs, one for tx (raw and hash) and one for blocks? (But that's just a thought - having all four is totally fine for me if someone has use for separate values.)
there is still a big potential message size difference between just hash and raw{block,tx} so i think having them separate still is better. it just gives people the most flexibility. btw, thanks for the review!
-zmqpubrawtx=<address>;hwm=<hwm> perhaps?
It might also make sense to return the configured HWM in getzmqnotifications.
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
@domob1812 sure, added hwm to getzmqnotifications
Shouldn't set the option per address, not per notification type?
@promag there can only be one of each of the notification types and each can have only one address. i updated the messages to show the address too. while i was doing that, i also made all of the zmq log messages prefix with "zmq: " because some were missing that.
for what it's worth, i tested synching a bitcoin core node that was 5 days behind with a zmqpubhashtx tcp socket that PUBlished to a ODROID-C1+ SUBscriber across town (with roughly 15-20ms ping RTT) and -zmqpubhashtxhwm=20000 allowed me to get no gaps in transaction hashes. i had gaps (i.e.- dropped messages) with zmqpubhashtxhwm=10000. this is by no means scientific, but does give some idea of what kinds of message backlogs people could possibly be dealing with.
99 | @@ -98,11 +100,11 @@ bool CZMQNotificationInterface::Initialize() 100 | CZMQAbstractNotifier *notifier = *i; 101 | if (notifier->Initialize(pcontext)) 102 | { 103 | - LogPrint(BCLog::ZMQ, " Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress()); 104 | + LogPrint(BCLog::ZMQ, "zmq: Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress());
I think this change makes sense - but it is unrelated to your other change, so perhaps move it at least to a separate commit?
like a separate pull request? i know what you mean, and for a personal repo i would, but my past experience here is that if i have more than one commit, nothing will get merged until i squash it down to one.
That's not true, just look at merged PR's. In this case I agree that a commit with this change is fine. However I would prefer to have it in a separate PR, precisely to avoid noise like this. Generally if a commit is not required in a PR, then I push it in a different PR.
@promag there can only be one of each of the notification types and each can have only one address. @mruddy right, at some point I was going to add support for multiple notifications of the same type but for some reason I don't remember why I quit that, so that got me confused.
My suggestion for the long term is to extend the existing argument, like:
-zmqpubhashtx=tcp://127.0.0.1:28332,hwm=20000,foo=bar,...
And this would allow multiple pubhashtx with different high water mark and other options.
Given the above, for now, a global/default hwm looks enough, and the above could be implemented later.
40 | @@ -39,6 +41,7 @@ class CZMQAbstractNotifier 41 | void *psocket; 42 | std::string type; 43 | std::string address; 44 | + int outbound_message_high_water_mark; // aka SNDHWM
outbound_message_high_water_mark not initialized in the constructor?
thanks. it was set, just not by the constructor. i changed things around to address that. i also found that CZMQAbstractPublishNotifier.nSequence was not initialized, so i initialized that too now.
rebased and updated the functional test just a moment ago
rebased and added a comma in the getzmqnotifications help message
29 | @@ -28,6 +30,12 @@ class CZMQAbstractNotifier 30 | void SetType(const std::string &t) { type = t; } 31 | std::string GetAddress() const { return address; } 32 | void SetAddress(const std::string &a) { address = a; } 33 | + int GetOutboundMessageHighWaterMark() const { return outbound_message_high_water_mark; } 34 | + void SetOutboundMessageHighWaterMark(const int sndhwm) { 35 | + if ((sndhwm >= 0) && (sndhwm <= std::numeric_limits<int>::max())) {
2018-09-25 21:25:16 clang(pr=14060): zmq/zmqabstractnotifier.h:35:38: warning: result of comparison 'const int' <= 2147483647 is always true [-Wtautological-type-limit-compare]
Use static_assert instead?
ok, i'll just check it like zmq checks it: https://github.com/zeromq/libzmq/blob/master/src/options.cpp#L310-L315
also rebased. thanks!
58 | @@ -59,6 +59,7 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create() 59 | CZMQAbstractNotifier *notifier = factory(); 60 | notifier->SetType(entry.first); 61 | notifier->SetAddress(address); 62 | + notifier->SetOutboundMessageHighWaterMark(gArgs.GetArg(arg + "hwm", CZMQAbstractNotifier::DEFAULT_ZMQ_SNDHWM));
2018-09-25 21:25:16 clang(pr=14060): zmq/zmqnotificationinterface.cpp:62:61: warning: implicit conversion loses integer precision: 'int64_t' (aka 'long') to 'int' [-Wshorten-64-to-32]
explicit cast added
rebased
Will this make ZMQ publishing blocking on the bitcoind side when used? If so, that needs clear documentation.
Will this make ZMQ publishing blocking on the
bitcoindside when used? If so, that needs clear documentation.
No, it will not. To add more color, without this PR, the SNDHWM is 1000. These new options just allow changing that value to something else. The drop/block behavior does not change. The motivation for this change was dropped messages. And from the ZMQ API docs, we see that the documented behavior is to drop (which is what we want, and what was experienced as the motivation): 'When a ZMQ_PUB socket enters the mute state due to having reached the high water mark for a subscriber, then any messages that would be sent to the subscriber in question shall instead be dropped until the mute state ends."
ACK
Tested. Works great
Came across this while seeing a flood of (apparently innocuous) "Unable to receive ZMQ rawtx message: ..." messages from Lnd. Can you add a release note in a followup PR?