Skip to content

Commit faff7f1

Browse files
Rahmeen14Rahmeen14
authored andcommitted
Model Host Health Monitoring choice on RabbitContextOptions
1 parent 059afaf commit faff7f1

5 files changed

Lines changed: 206 additions & 26 deletions

File tree

src/rmq/rmqa/rmqa_rabbitcontextimpl.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <ball_log.h>
3939
#include <bdlf_bind.h>
4040
#include <bdlmt_threadpool.h>
41+
#include <bsl_variant.h>
4142
#include <bsls_review.h>
4243
#include <bsls_timeinterval.h>
4344

@@ -191,16 +192,21 @@ RabbitContextImpl::RabbitContextImpl(
191192
, d_producerTracing(options.producerTracing())
192193
{
193194

194-
const bool isHostHealthMonitoringEnabled =
195-
options.hostHealthConfig().has_value();
195+
// Host health monitoring runs only when a config has been selected. An
196+
// explicit opt-out (HostHealthAwarenessOff) and an unset selection both
197+
// leave no config, so no monitor is created -- preserving the behaviour
198+
// from before an opt-out could be expressed: monitoring ran whenever a
199+
// config was attached. get_if returns a pointer into the selection owned by
200+
// `options` (which outlives this constructor), or null for the opt-out and
201+
// unset alternatives.
202+
const rmqt::HostHealthConfig* hostHealthConfig =
203+
bsl::get_if<rmqt::HostHealthConfig>(&options.hostHealthSelection());
204+
const bool isHostHealthMonitoringEnabled = hostHealthConfig != 0;
196205

197206
// Host health monitoring enabled
198207
if (isHostHealthMonitoringEnabled) {
199-
const rmqt::HostHealthConfig& hostHealthConfig =
200-
*options.hostHealthConfig();
201-
202208
d_hostHealthMonitor = bsl::make_shared<rmqamqp::HostHealthMonitor>(
203-
hostHealthConfig, d_metricPublisher.get());
209+
*hostHealthConfig, d_metricPublisher.get());
204210
d_hostHealthMonitor->start(d_eventLoop->timerFactory());
205211
}
206212

src/rmq/rmqa/rmqa_rabbitcontextoptions.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,18 @@ void defaultErrorCallback(const bsl::string& err, int rc)
6262
} // namespace
6363

6464
RabbitContextOptions::RabbitContextOptions()
65-
: d_threadpool()
66-
, d_onError(bdlf::BindUtil::bind(&defaultErrorCallback,
65+
: d_onError(bdlf::BindUtil::bind(&defaultErrorCallback,
6766
bdlf::PlaceHolders::_1,
6867
bdlf::PlaceHolders::_2))
68+
, d_hostHealthSelection(HostHealthAwarenessUnset())
69+
, d_threadpool()
6970
, d_metricPublisher()
70-
, d_clientProperties()
7171
, d_messageProcessingTimeout(DEFAULT_MESSAGE_PROCESSING_TIMEOUT)
72-
, d_tunables()
7372
, d_connectionErrorThreshold()
7473
, d_connectionEstablishmentTimeout()
74+
, d_clientProperties()
75+
, d_tunables()
7576
, d_shuffleConnectionEndpoints()
76-
, d_hostHealthConfig()
7777
{
7878
populateUsefulInformation(&d_clientProperties);
7979
}
@@ -174,7 +174,15 @@ RabbitContextOptions& RabbitContextOptions::setShuffleConnectionEndpoints(
174174
RabbitContextOptions& RabbitContextOptions::setHostHealthConfig(
175175
const rmqt::HostHealthConfig& hostHealthConfig)
176176
{
177-
d_hostHealthConfig = hostHealthConfig;
177+
// A config is one alternative of the selection; delegate so the variant
178+
// setter remains the single writer of d_hostHealthSelection.
179+
return setHostHealthSelection(hostHealthConfig);
180+
}
181+
182+
RabbitContextOptions& RabbitContextOptions::setHostHealthSelection(
183+
const HostHealthSelection& selection)
184+
{
185+
d_hostHealthSelection = selection;
178186
return *this;
179187
}
180188

src/rmq/rmqa/rmqa_rabbitcontextoptions.h

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828

2929
#include <bdlmt_threadpool.h>
3030
#include <bsl_memory.h>
31+
#include <bsl_optional.h>
3132
#include <bsl_set.h>
33+
#include <bsl_variant.h>
3234
#include <bsls_timeinterval.h>
3335

3436
namespace BloombergLP {
@@ -44,6 +46,30 @@ class RabbitContextOptions {
4446
public:
4547
typedef bsl::set<bsl::string> Tunables;
4648

49+
/// \brief Tag type expressing no host health preference: it neither opts in
50+
/// nor opts out, leaving the choice to enclosing configuration layers. This
51+
/// is the alternative held by a default-constructed \c HostHealthSelection
52+
/// (the default). See \c setHostHealthSelection.
53+
struct HostHealthAwarenessUnset {};
54+
55+
/// \brief Tag type selecting an explicit opt-out from host health
56+
/// awareness; see \c setHostHealthSelection. Selecting it is distinct from
57+
/// leaving the host health selection unset (\c HostHealthAwarenessUnset),
58+
/// which expresses no preference and lets enclosing configuration layers
59+
/// apply their own policy.
60+
struct HostHealthAwarenessOff {};
61+
62+
/// \brief A caller's host health selection: exactly one of three mutually
63+
/// exclusive alternatives -- \c HostHealthAwarenessUnset (no preference;
64+
/// the default), \c rmqt::HostHealthConfig (opt-in; monitoring runs), or
65+
/// \c HostHealthAwarenessOff (explicit opt-out). A default-constructed
66+
/// selection holds \c HostHealthAwarenessUnset. See
67+
/// \c setHostHealthSelection.
68+
typedef bsl::variant<HostHealthAwarenessUnset,
69+
rmqt::HostHealthConfig,
70+
HostHealthAwarenessOff>
71+
HostHealthSelection;
72+
4773
/// \brief By Default RabbitContext will
4874
/// 1) Create it's own threadpool for
4975
/// calling back to client code e.g. consuming messages, confirming
@@ -177,9 +203,25 @@ class RabbitContextOptions {
177203
/// config is not set, \c consumeOnlyFromHealthyHost has no effect.
178204
///
179205
/// \param hostHealthConfig configuration for host health monitoring
206+
///
207+
/// \note This is a convenience equivalent to \c setHostHealthSelection with
208+
/// a \c HostHealthConfig: selecting a config is what causes the monitor to
209+
/// run, and it replaces any prior opt-out selection. Leaving the selection
210+
/// unset (the default) expresses no preference.
180211
RabbitContextOptions&
181212
setHostHealthConfig(const rmqt::HostHealthConfig& hostHealthConfig);
182213

214+
/// \brief Set the caller's host health selection: a \c HostHealthConfig to
215+
/// opt in (monitoring runs) or \c HostHealthAwarenessOff to explicitly
216+
/// opt out (no monitor is created even if a config could otherwise be
217+
/// attached). The alternatives are mutually exclusive -- this replaces any
218+
/// previously set config or opt-out. Not calling this at all leaves the
219+
/// selection unset (the default), which expresses no preference and lets
220+
/// enclosing configuration layers apply their own policy.
221+
/// \param selection the host health selection
222+
RabbitContextOptions&
223+
setHostHealthSelection(const HostHealthSelection& selection);
224+
183225
bdlmt::ThreadPool* threadpool() const { return d_threadpool; }
184226

185227
const bsl::shared_ptr<rmqp::MetricPublisher>& metricPublisher() const
@@ -227,32 +269,38 @@ class RabbitContextOptions {
227269
return d_shuffleConnectionEndpoints;
228270
}
229271

230-
/// \brief Get the host health config. If not set, host health monitoring is
231-
/// disabled. By default, host health monitoring is disabled.
232-
/// \return The host health config
233-
const bsl::optional<rmqt::HostHealthConfig>& hostHealthConfig() const
272+
/// \brief Get the caller's host health selection. The returned variant
273+
/// holds \c HostHealthAwarenessUnset when no preference has been expressed
274+
/// (the default), a \c rmqt::HostHealthConfig when opted in (via
275+
/// \c setHostHealthSelection or \c setHostHealthConfig), or a
276+
/// \c HostHealthAwarenessOff when explicitly opted out.
277+
/// \return The host health selection
278+
const HostHealthSelection& hostHealthSelection() const
234279
{
235-
return d_hostHealthConfig;
280+
return d_hostHealthSelection;
236281
}
237282

238283
#ifdef USES_LIBRMQ_EXPERIMENTAL_FEATURES
239284
RabbitContextOptions& setTunable(const bsl::string& tunable);
240285
#endif
241286

242287
private:
288+
// Members are ordered to minimize padding (see
289+
// clang-analyzer-optin.performance.Padding), not by logical grouping. The
290+
// constructor's initializer list mirrors this order to avoid -Wreorder.
243291
static const int DEFAULT_MESSAGE_PROCESSING_TIMEOUT = 60;
244-
bdlmt::ThreadPool* d_threadpool;
245292
rmqt::ErrorCallback d_onError;
293+
HostHealthSelection d_hostHealthSelection;
294+
bdlmt::ThreadPool* d_threadpool;
246295
bsl::shared_ptr<rmqp::MetricPublisher> d_metricPublisher;
247-
rmqt::FieldTable d_clientProperties;
248296
bsls::TimeInterval d_messageProcessingTimeout;
249-
rmqt::Tunables d_tunables;
250-
bsl::optional<bsls::TimeInterval> d_connectionErrorThreshold;
251-
bsl::optional<bsls::TimeInterval> d_connectionEstablishmentTimeout;
252297
bsl::shared_ptr<rmqp::ConsumerTracing> d_consumerTracing;
253298
bsl::shared_ptr<rmqp::ProducerTracing> d_producerTracing;
299+
bsl::optional<bsls::TimeInterval> d_connectionErrorThreshold;
300+
bsl::optional<bsls::TimeInterval> d_connectionEstablishmentTimeout;
301+
rmqt::FieldTable d_clientProperties;
302+
rmqt::Tunables d_tunables;
254303
bsl::optional<bool> d_shuffleConnectionEndpoints;
255-
bsl::optional<rmqt::HostHealthConfig> d_hostHealthConfig;
256304
};
257305

258306
} // namespace rmqa

src/tests/rmqa/rmqa_rabbitcontextimpl.t.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include <bdlmt_threadpool.h>
3333
#include <bsl_memory.h>
34+
#include <bsl_variant.h>
3435
#include <bslma_managedptr.h>
3536

3637
using namespace BloombergLP;
@@ -97,10 +98,47 @@ class RabbitContextImplTests : public Test {
9798

9899
TEST_F(RabbitContextImplTests, ItsAlive)
99100
{
101+
// d_options has a host health config selected, so the monitor is created --
102+
// timerFactory() is called three times (monitor start, connection factory,
103+
// connection watchdog).
100104
createExpectations();
101105
rmqa::RabbitContextImpl context(getMockEventLoop(), d_options);
102106
}
103107

108+
TEST_F(RabbitContextImplTests, LegacyUserWithConfigStillMonitors)
109+
{
110+
// Backward-compatibility guarantee: a caller who enabled monitoring before
111+
// an explicit opt-out could be expressed did so by attaching a host health
112+
// config, and did not opt out. They must keep being monitored -- the
113+
// monitor is still created (three timerFactory() calls).
114+
ASSERT_TRUE(bsl::holds_alternative<rmqt::HostHealthConfig>(
115+
d_options.hostHealthSelection()));
116+
117+
createExpectations();
118+
rmqa::RabbitContextImpl context(getMockEventLoop(), d_options);
119+
}
120+
121+
TEST_F(RabbitContextImplTests, HostHealthMonitoringOptOutSuppressesMonitor)
122+
{
123+
// Even though d_options had a host health config attached, an explicit
124+
// opt-out selection replaces it and prevents the monitor from being
125+
// created. The monitor is what makes the third timerFactory() call (its
126+
// start()), so without it timerFactory() is called only twice (connection
127+
// factory + watchdog).
128+
d_options.setHostHealthSelection(
129+
rmqa::RabbitContextOptions::HostHealthAwarenessOff());
130+
ASSERT_FALSE(bsl::holds_alternative<rmqt::HostHealthConfig>(
131+
d_options.hostHealthSelection()));
132+
133+
EXPECT_CALL(*d_mockEventLoop, resolver(false)).Times(1);
134+
EXPECT_CALL(*d_mockEventLoop, timerFactory())
135+
.Times(2)
136+
.WillRepeatedly(Return(d_mockTimerFactory));
137+
EXPECT_CALL(*d_mockEventLoop, waitForEventLoopExit(_)).Times(1);
138+
139+
rmqa::RabbitContextImpl context(getMockEventLoop(), d_options);
140+
}
141+
104142
TEST_F(RabbitContextImplTests, ErrorWhenProvideNullEndpoint)
105143
{
106144
createExpectations();

src/tests/rmqa/rmqa_rabbitcontextoptions.t.cpp

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
#include <bsls_timeinterval.h>
2222

2323
#include <bsl_optional.h>
24+
#include <bsl_variant.h>
2425

26+
#include <gmock/gmock.h>
2527
#include <gtest/gtest.h>
2628

2729
using namespace BloombergLP;
@@ -31,6 +33,33 @@ using namespace ::testing;
3133
namespace {
3234
bool alwaysHealthy() { return true; }
3335

36+
// gmock matchers over a RabbitContextOptions' host health selection, so
37+
// assertions read EXPECT_THAT(options, isOptOut()) / Not(isConfig()) etc. `arg`
38+
// is the matched RabbitContextOptions.
39+
40+
/// Matches when the selection is an explicit opt-out.
41+
MATCHER(isOptOut, "holds an explicit host health opt-out")
42+
{
43+
return bsl::holds_alternative<
44+
rmqa::RabbitContextOptions::HostHealthAwarenessOff>(
45+
arg.hostHealthSelection());
46+
}
47+
48+
/// Matches when no host health preference is expressed (the default).
49+
MATCHER(isUnset, "expresses no host health preference")
50+
{
51+
return bsl::holds_alternative<
52+
rmqa::RabbitContextOptions::HostHealthAwarenessUnset>(
53+
arg.hostHealthSelection());
54+
}
55+
56+
/// Matches when a host health config is selected (opt-in).
57+
MATCHER(isConfig, "holds a host health config")
58+
{
59+
return bsl::holds_alternative<rmqt::HostHealthConfig>(
60+
arg.hostHealthSelection());
61+
}
62+
3463
/// `bsls_asserttest`'s `BSLS_ASSERTTEST_ASSERT_{PASS,FAIL}` macros report their
3564
/// outcome by calling a driver-supplied `ASSERT(bool)` (which its header allows
3665
/// to be a function, not just a macro). Route that into a gtest expectation.
@@ -43,7 +72,10 @@ TEST(RabbitContextOptions, Defaults)
4372
rmqa::RabbitContextOptions t;
4473
EXPECT_FALSE(t.metricPublisher());
4574
EXPECT_FALSE(t.threadpool());
46-
EXPECT_FALSE(t.hostHealthConfig().has_value());
75+
EXPECT_THAT(t, Not(isConfig()));
76+
// No host health selection is expressed by default: neither a config nor
77+
// an explicit opt-out.
78+
EXPECT_THAT(t, isUnset());
4779
t.errorCallback()("heres an error", -1);
4880
}
4981

@@ -52,7 +84,7 @@ TEST(RabbitContextOptions, SetHostHealthConfig)
5284
rmqa::RabbitContextOptions options;
5385

5486
// Initially not set
55-
EXPECT_FALSE(options.hostHealthConfig().has_value());
87+
EXPECT_THAT(options, Not(isConfig()));
5688

5789
// Create a health checker function
5890
rmqt::HostHealthConfig config(alwaysHealthy);
@@ -61,7 +93,55 @@ TEST(RabbitContextOptions, SetHostHealthConfig)
6193
options.setHostHealthConfig(config);
6294

6395
// Now it should be set
64-
EXPECT_TRUE(options.hostHealthConfig().has_value());
96+
EXPECT_THAT(options, isConfig());
97+
}
98+
99+
TEST(RabbitContextOptions, SetHostHealthSelectionOptOut)
100+
{
101+
rmqa::RabbitContextOptions options;
102+
103+
// Initially no selection is expressed (distinct from an explicit opt-out).
104+
EXPECT_THAT(options, Not(isConfig()));
105+
EXPECT_THAT(options, isUnset());
106+
107+
// Explicit opt-out.
108+
options.setHostHealthSelection(
109+
RabbitContextOptions::HostHealthAwarenessOff());
110+
EXPECT_THAT(options, isOptOut());
111+
EXPECT_THAT(options, Not(isConfig()));
112+
}
113+
114+
TEST(RabbitContextOptions, SetHostHealthSelectionConfig)
115+
{
116+
rmqa::RabbitContextOptions options;
117+
118+
// Selecting a config via the variant setter is equivalent to
119+
// setHostHealthConfig: the config is retrievable and it is not an opt-out.
120+
options.setHostHealthSelection(rmqt::HostHealthConfig(alwaysHealthy));
121+
EXPECT_THAT(options, isConfig());
122+
EXPECT_THAT(options, Not(isOptOut()));
123+
}
124+
125+
TEST(RabbitContextOptions, HostHealthSelectionIsMutuallyExclusive)
126+
{
127+
// A config and an explicit opt-out are two alternatives of a single
128+
// selection, so setting one replaces the other -- the contradictory
129+
// "config attached AND opted out" state cannot be expressed.
130+
rmqa::RabbitContextOptions options;
131+
132+
// Opt-out then attach a config: the config wins.
133+
options.setHostHealthSelection(
134+
RabbitContextOptions::HostHealthAwarenessOff());
135+
options.setHostHealthConfig(rmqt::HostHealthConfig(alwaysHealthy));
136+
EXPECT_THAT(options, isConfig());
137+
EXPECT_THAT(options, Not(isOptOut()));
138+
139+
// Attach a config then opt out: the opt-out wins.
140+
options.setHostHealthConfig(rmqt::HostHealthConfig(alwaysHealthy));
141+
options.setHostHealthSelection(
142+
RabbitContextOptions::HostHealthAwarenessOff());
143+
EXPECT_THAT(options, isOptOut());
144+
EXPECT_THAT(options, Not(isConfig()));
65145
}
66146

67147
TEST(RabbitContextOptions, SetConnectionEstablishmentTimeoutAcceptsValid)

0 commit comments

Comments
 (0)