Skip to content

Commit 6c428c5

Browse files
committed
fix: give FDv1 adapter a private status manager
1 parent 27f5c1e commit 6c428c5

3 files changed

Lines changed: 70 additions & 75 deletions

File tree

libs/server-sdk/src/data_systems/fdv2/fdv1_adapter_synchronizer.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,11 @@ std::string const& FDv1AdapterSynchronizer::ConvertingDestination::Identity()
121121

122122
// ----- FDv1AdapterSynchronizer -----
123123

124-
FDv1AdapterSynchronizer::FDv1AdapterSynchronizer(
125-
std::unique_ptr<data_interfaces::IDataSynchronizer> fdv1_source,
126-
data_components::DataSourceStatusManager* status_manager)
124+
FDv1AdapterSynchronizer::FDv1AdapterSynchronizer(SourceBuilder source_builder)
127125
: state_(std::make_shared<State>(close_promise_.GetFuture())),
128126
destination_(std::make_unique<ConvertingDestination>(state_)),
129-
status_manager_(status_manager),
127+
status_manager_(
128+
std::make_unique<data_components::DataSourceStatusManager>()),
130129
status_subscription_(status_manager_->OnDataSourceStatusChange(
131130
[state = state_](DataSourceStatus status) {
132131
auto error = status.LastError();
@@ -148,7 +147,7 @@ FDv1AdapterSynchronizer::FDv1AdapterSynchronizer(
148147
break;
149148
}
150149
})),
151-
fdv1_source_(std::move(fdv1_source)) {}
150+
fdv1_source_(source_builder(*status_manager_)) {}
152151

153152
FDv1AdapterSynchronizer::~FDv1AdapterSynchronizer() {
154153
Close();

libs/server-sdk/src/data_systems/fdv2/fdv1_adapter_synchronizer.hpp

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <launchdarkly/connection.hpp>
1010

1111
#include <deque>
12+
#include <functional>
1213
#include <memory>
1314
#include <mutex>
1415
#include <optional>
@@ -34,17 +35,17 @@ namespace launchdarkly::server_side::data_systems {
3435
class FDv1AdapterSynchronizer final
3536
: public data_interfaces::IFDv2Synchronizer {
3637
public:
38+
using SourceBuilder =
39+
std::function<std::unique_ptr<data_interfaces::IDataSynchronizer>(
40+
data_components::DataSourceStatusManager&)>;
41+
3742
/**
38-
* @param fdv1_source The wrapped source. Must have been constructed
39-
* with status_manager as its status sink so that
40-
* state changes flow back into this adapter.
41-
* @param status_manager Non-owning. The caller retains ownership and
42-
* must keep it alive for the lifetime of the
43-
* wrapped source and this adapter.
43+
* @param source_builder Called once during construction with the
44+
* adapter's status manager. Returns the wrapped
45+
* FDv1 source, which must be constructed against
46+
* the provided manager as its status sink.
4447
*/
45-
FDv1AdapterSynchronizer(
46-
std::unique_ptr<data_interfaces::IDataSynchronizer> fdv1_source,
47-
data_components::DataSourceStatusManager* status_manager);
48+
explicit FDv1AdapterSynchronizer(SourceBuilder source_builder);
4849

4950
~FDv1AdapterSynchronizer() override;
5051

@@ -111,10 +112,8 @@ class FDv1AdapterSynchronizer final
111112
std::shared_ptr<State> const state_;
112113
std::unique_ptr<ConvertingDestination> const destination_;
113114

114-
// Non-owning. The caller must keep this alive for the lifetime of
115-
// the wrapped source, which holds a reference to it for status
116-
// reporting.
117-
data_components::DataSourceStatusManager* const status_manager_;
115+
std::unique_ptr<data_components::DataSourceStatusManager> const
116+
status_manager_;
118117
std::unique_ptr<IConnection> const status_subscription_;
119118

120119
std::unique_ptr<data_interfaces::IDataSynchronizer> const fdv1_source_;

libs/server-sdk/tests/fdv1_adapter_synchronizer_test.cpp

Lines changed: 54 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -47,41 +47,53 @@ class MockFDv1Source final : public IDataSynchronizer {
4747
bool bootstrap_was_null = false;
4848
};
4949

50+
// Returns a SourceBuilder closure that constructs a MockFDv1Source. The
51+
// resulting source and the adapter's internal status manager are exposed
52+
// through the out parameters; pass nullptr for either to skip.
53+
FDv1AdapterSynchronizer::SourceBuilder MakeMockBuilder(
54+
MockFDv1Source** out_source = nullptr,
55+
DataSourceStatusManager** out_sm = nullptr) {
56+
return [out_source, out_sm](DataSourceStatusManager& sm) {
57+
if (out_sm) {
58+
*out_sm = &sm;
59+
}
60+
auto source = std::make_unique<MockFDv1Source>(sm);
61+
if (out_source) {
62+
*out_source = source.get();
63+
}
64+
return source;
65+
};
66+
}
67+
5068
} // namespace
5169

5270
TEST(FDv1AdapterSynchronizerTest, FirstNextStartsFDv1Source) {
53-
DataSourceStatusManager status_manager;
54-
auto source = std::make_unique<MockFDv1Source>(status_manager);
55-
auto* source_ptr = source.get();
56-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
71+
MockFDv1Source* source = nullptr;
72+
FDv1AdapterSynchronizer adapter(MakeMockBuilder(&source));
5773

5874
auto future = adapter.Next(data_model::Selector{});
5975

60-
EXPECT_EQ(1, source_ptr->start_count);
61-
EXPECT_TRUE(source_ptr->bootstrap_was_null);
76+
EXPECT_EQ(1, source->start_count);
77+
EXPECT_TRUE(source->bootstrap_was_null);
6278
EXPECT_FALSE(future.IsFinished());
6379
}
6480

6581
TEST(FDv1AdapterSynchronizerTest, SecondNextDoesNotRestartSource) {
66-
DataSourceStatusManager status_manager;
67-
auto source = std::make_unique<MockFDv1Source>(status_manager);
68-
auto* source_ptr = source.get();
69-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
82+
MockFDv1Source* source = nullptr;
83+
FDv1AdapterSynchronizer adapter(MakeMockBuilder(&source));
7084

7185
auto first = adapter.Next(data_model::Selector{});
72-
source_ptr->destination_->Init(data_model::SDKDataSet{});
86+
source->destination_->Init(data_model::SDKDataSet{});
7387
auto result = first.WaitForResult(1s);
7488
ASSERT_TRUE(result.has_value());
7589
adapter.Next(data_model::Selector{});
7690

77-
EXPECT_EQ(1, source_ptr->start_count);
91+
EXPECT_EQ(1, source->start_count);
7892
}
7993

8094
TEST(FDv1AdapterSynchronizerTest, FDv1InitProducesFullChangeSet) {
81-
DataSourceStatusManager status_manager;
82-
auto source = std::make_unique<MockFDv1Source>(status_manager);
83-
auto* source_ptr = source.get();
84-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
95+
MockFDv1Source* source = nullptr;
96+
FDv1AdapterSynchronizer adapter(MakeMockBuilder(&source));
8597

8698
auto future = adapter.Next(data_model::Selector{});
8799

@@ -90,7 +102,7 @@ TEST(FDv1AdapterSynchronizerTest, FDv1InitProducesFullChangeSet) {
90102
flag.key = "flagA";
91103
flag.version = 1;
92104
data_set.flags.emplace("flagA", data_model::FlagDescriptor(flag));
93-
source_ptr->destination_->Init(std::move(data_set));
105+
source->destination_->Init(std::move(data_set));
94106

95107
auto result = future.WaitForResult(1s);
96108
ASSERT_TRUE(result.has_value());
@@ -104,17 +116,15 @@ TEST(FDv1AdapterSynchronizerTest, FDv1InitProducesFullChangeSet) {
104116
}
105117

106118
TEST(FDv1AdapterSynchronizerTest, FDv1UpsertProducesPartialChangeSet) {
107-
DataSourceStatusManager status_manager;
108-
auto source = std::make_unique<MockFDv1Source>(status_manager);
109-
auto* source_ptr = source.get();
110-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
119+
MockFDv1Source* source = nullptr;
120+
FDv1AdapterSynchronizer adapter(MakeMockBuilder(&source));
111121

112122
// Flag upsert.
113123
auto flag_future = adapter.Next(data_model::Selector{});
114124
data_model::Flag flag;
115125
flag.key = "flagA";
116126
flag.version = 2;
117-
source_ptr->destination_->Upsert("flagA", data_model::FlagDescriptor(flag));
127+
source->destination_->Upsert("flagA", data_model::FlagDescriptor(flag));
118128

119129
auto flag_result = flag_future.WaitForResult(1s);
120130
ASSERT_TRUE(flag_result.has_value());
@@ -131,8 +141,7 @@ TEST(FDv1AdapterSynchronizerTest, FDv1UpsertProducesPartialChangeSet) {
131141
data_model::Segment seg;
132142
seg.key = "segA";
133143
seg.version = 3;
134-
source_ptr->destination_->Upsert("segA",
135-
data_model::SegmentDescriptor(seg));
144+
source->destination_->Upsert("segA", data_model::SegmentDescriptor(seg));
136145

137146
auto seg_result = seg_future.WaitForResult(1s);
138147
ASSERT_TRUE(seg_result.has_value());
@@ -146,9 +155,7 @@ TEST(FDv1AdapterSynchronizerTest, FDv1UpsertProducesPartialChangeSet) {
146155
}
147156

148157
TEST(FDv1AdapterSynchronizerTest, ClosePendingNextReturnsShutdown) {
149-
DataSourceStatusManager status_manager;
150-
auto source = std::make_unique<MockFDv1Source>(status_manager);
151-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
158+
FDv1AdapterSynchronizer adapter(MakeMockBuilder());
152159

153160
auto future = adapter.Next(data_model::Selector{});
154161
EXPECT_FALSE(future.IsFinished());
@@ -162,48 +169,42 @@ TEST(FDv1AdapterSynchronizerTest, ClosePendingNextReturnsShutdown) {
162169
}
163170

164171
TEST(FDv1AdapterSynchronizerTest, CloseShutsDownStartedFDv1Source) {
165-
DataSourceStatusManager status_manager;
166-
auto source = std::make_unique<MockFDv1Source>(status_manager);
167-
auto* source_ptr = source.get();
168-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
172+
MockFDv1Source* source = nullptr;
173+
FDv1AdapterSynchronizer adapter(MakeMockBuilder(&source));
169174

170175
adapter.Next(data_model::Selector{});
171176
adapter.Close();
172177

173-
EXPECT_EQ(1, source_ptr->shutdown_count);
178+
EXPECT_EQ(1, source->shutdown_count);
174179
}
175180

176181
TEST(FDv1AdapterSynchronizerTest, CloseWithoutStartDoesNotShutDownFDv1Source) {
177-
DataSourceStatusManager status_manager;
178-
auto source = std::make_unique<MockFDv1Source>(status_manager);
179-
auto* source_ptr = source.get();
180-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
182+
MockFDv1Source* source = nullptr;
183+
FDv1AdapterSynchronizer adapter(MakeMockBuilder(&source));
181184

182185
// No Next() call — FDv1 source was never started.
183186
adapter.Close();
184187

185-
EXPECT_EQ(0, source_ptr->start_count);
186-
EXPECT_EQ(0, source_ptr->shutdown_count);
188+
EXPECT_EQ(0, source->start_count);
189+
EXPECT_EQ(0, source->shutdown_count);
187190
}
188191

189192
TEST(FDv1AdapterSynchronizerTest, QueuedResultsDrainInFifoOrder) {
190-
DataSourceStatusManager status_manager;
191-
auto source = std::make_unique<MockFDv1Source>(status_manager);
192-
auto* source_ptr = source.get();
193-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
193+
MockFDv1Source* source = nullptr;
194+
FDv1AdapterSynchronizer adapter(MakeMockBuilder(&source));
194195

195196
// Start the source by satisfying one Next() with an Init.
196197
auto first = adapter.Next(data_model::Selector{});
197-
source_ptr->destination_->Init(data_model::SDKDataSet{});
198+
source->destination_->Init(data_model::SDKDataSet{});
198199
first.WaitForResult(1s);
199200

200201
// Two upserts queue with no Next() in flight.
201202
data_model::Flag flag_a;
202203
flag_a.key = "a";
203204
data_model::Flag flag_b;
204205
flag_b.key = "b";
205-
source_ptr->destination_->Upsert("a", data_model::FlagDescriptor(flag_a));
206-
source_ptr->destination_->Upsert("b", data_model::FlagDescriptor(flag_b));
206+
source->destination_->Upsert("a", data_model::FlagDescriptor(flag_a));
207+
source->destination_->Upsert("b", data_model::FlagDescriptor(flag_b));
207208

208209
// Drain in FIFO order.
209210
auto r1 = adapter.Next(data_model::Selector{}).WaitForResult(1s);
@@ -221,15 +222,14 @@ TEST(FDv1AdapterSynchronizerTest, QueuedResultsDrainInFifoOrder) {
221222
}
222223

223224
TEST(FDv1AdapterSynchronizerTest, InterruptedStatusProducesInterruptedResult) {
224-
DataSourceStatusManager status_manager;
225-
auto source = std::make_unique<MockFDv1Source>(status_manager);
226-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
225+
DataSourceStatusManager* source_manager = nullptr;
226+
FDv1AdapterSynchronizer adapter(MakeMockBuilder(nullptr, &source_manager));
227227

228228
// kInterrupted from kInitializing stays kInitializing; drive past first.
229-
status_manager.SetState(DataSourceStatus::DataSourceState::kValid);
229+
source_manager->SetState(DataSourceStatus::DataSourceState::kValid);
230230

231231
auto future = adapter.Next(data_model::Selector{});
232-
status_manager.SetState(
232+
source_manager->SetState(
233233
DataSourceStatus::DataSourceState::kInterrupted,
234234
DataSourceStatus::ErrorInfo::ErrorKind::kNetworkError, "boom");
235235

@@ -243,12 +243,11 @@ TEST(FDv1AdapterSynchronizerTest, InterruptedStatusProducesInterruptedResult) {
243243
}
244244

245245
TEST(FDv1AdapterSynchronizerTest, OffStatusProducesTerminalErrorResult) {
246-
DataSourceStatusManager status_manager;
247-
auto source = std::make_unique<MockFDv1Source>(status_manager);
248-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
246+
DataSourceStatusManager* source_manager = nullptr;
247+
FDv1AdapterSynchronizer adapter(MakeMockBuilder(nullptr, &source_manager));
249248

250249
auto future = adapter.Next(data_model::Selector{});
251-
status_manager.SetState(
250+
source_manager->SetState(
252251
DataSourceStatus::DataSourceState::kOff,
253252
DataSourceStatus::ErrorInfo::ErrorKind::kErrorResponse, "401");
254253

@@ -262,9 +261,7 @@ TEST(FDv1AdapterSynchronizerTest, OffStatusProducesTerminalErrorResult) {
262261
}
263262

264263
TEST(FDv1AdapterSynchronizerTest, NextAfterCloseReturnsShutdown) {
265-
DataSourceStatusManager status_manager;
266-
auto source = std::make_unique<MockFDv1Source>(status_manager);
267-
FDv1AdapterSynchronizer adapter(std::move(source), &status_manager);
264+
FDv1AdapterSynchronizer adapter(MakeMockBuilder());
268265

269266
adapter.Close();
270267
auto future = adapter.Next(data_model::Selector{});

0 commit comments

Comments
 (0)