Skip to content

Commit c5fd239

Browse files
authored
feat: expose Big Segments via public Client and Config APIs (#552)
# Expose Big Segments to customer code Makes the server-side Big Segments feature reachable from customer code: a public config method to wire a store, and a status provider on the client for querying store health and subscribing to changes. Builds on SDK-2366-2. ## What's in - Promotes `BigSegmentsBuilder` to the public include tree and adds `ConfigBuilder::BigSegments(BigSegmentsBuilder)`, surfaced as an optional `Config::BigSegments()`. - New public `IBigSegmentStoreStatusProvider` and `BigSegmentStoreStatus` (`IsAvailable()`/`IsStale()`), plus `Client::BigSegmentStoreStatus()`. - `ClientImpl` constructs the `BigSegmentStoreWrapper` from config, hands it to the evaluator, and starts its background poll. An internal adapter exposes the wrapper's status broadcaster as the public provider. - `BigSegmentsBuilder::Build()` now returns a new error code when the store is null. ## Design notes - The status surface mirrors `DataSourceStatus`: the accessor is `Status()`, and listeners use `OnBigSegmentStoreStatusChange(handler) -> IConnection`. - When no store is configured, the provider reports the store as unavailable and registered listeners never fire. ## Testing Unit tests added for the new behavior. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes flag evaluation wiring and add a new public config surface; misconfiguration or store outages can affect Big Segment targeting, though behavior is gated on optional config and mirrors existing data-source status patterns. > > **Overview** > This PR wires **Big Segments** into the public server SDK: customers can attach a store via config and observe store health on the client, similar to data source status. > > **Configuration:** `ConfigBuilder::BigSegments(BigSegmentsBuilder)` stores an optional `BigSegmentsConfig` on `Config`. `BigSegmentsBuilder::Build()` now returns `tl::expected` and fails with `kConfig_BigSegments_NullStore` when the store pointer is null (previously a null store was allowed through). > > **Client runtime:** When Big Segments are configured, `ClientImpl` creates `BigSegmentStoreWrapper`, passes it to the evaluator, starts background metadata polling, and exposes status through `Client::BigSegmentStoreStatus()`. > > **Public status API:** New `BigSegmentStoreStatus` (`IsAvailable` / `IsStale`) and `IBigSegmentStoreStatusProvider` with `Status()` and `OnBigSegmentStoreStatusChange` → `IConnection`. An internal adapter maps the wrapper to this API; with no store configured, status is unavailable/not stale and listeners are no-ops. > > Unit tests cover builder validation, config round-trip, and status provider behavior (including transitions). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 11902fa. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 42e8ecd commit c5fd239

21 files changed

Lines changed: 595 additions & 71 deletions

libs/common/include/launchdarkly/error.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ enum class Error : std::uint32_t {
2424
/* Client-side errors: 10000-19999 */
2525
/* Server-side errors: 20000-29999 */
2626
kConfig_DataSystem_LazyLoad_MissingSource = 20000,
27+
kConfig_BigSegments_NullStore = 20001,
2728
kMax = std::numeric_limits<std::uint32_t>::max()
2829
};
2930

libs/common/src/error.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ char const* ErrorToString(Error err) {
3232
return "sdk key: cannot be empty";
3333
case Error::kConfig_DataSystem_LazyLoad_MissingSource:
3434
return "data system: lazy load config requires a source";
35+
case Error::kConfig_BigSegments_NullStore:
36+
return "big segments: store must not be null";
3537
case Error::kMax:
3638
break;
3739
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#pragma once
2+
3+
#include <launchdarkly/connection.hpp>
4+
5+
#include <functional>
6+
#include <memory>
7+
#include <ostream>
8+
9+
namespace launchdarkly::server_side {
10+
11+
/**
12+
* The current health of a Big Segments store, independent of any single
13+
* context's membership.
14+
*/
15+
class BigSegmentStoreStatus {
16+
public:
17+
BigSegmentStoreStatus(bool available, bool stale);
18+
19+
/**
20+
* True if the most recent store query or metadata poll succeeded. If false,
21+
* Big Segments membership cannot currently be evaluated reliably.
22+
*/
23+
[[nodiscard]] bool IsAvailable() const;
24+
25+
/**
26+
* True if the store's data has not been updated within the configured
27+
* stale-after threshold. The data may still be queried; it is just older
28+
* than desired.
29+
*/
30+
[[nodiscard]] bool IsStale() const;
31+
32+
private:
33+
bool available_;
34+
bool stale_;
35+
};
36+
37+
bool operator==(BigSegmentStoreStatus const& a, BigSegmentStoreStatus const& b);
38+
bool operator!=(BigSegmentStoreStatus const& a, BigSegmentStoreStatus const& b);
39+
40+
/**
41+
* Interface for accessing and listening to the Big Segments store status.
42+
*/
43+
class IBigSegmentStoreStatusProvider {
44+
public:
45+
/**
46+
* The current status of the Big Segments store. If no store is configured,
47+
* reports unavailable and not stale.
48+
*/
49+
[[nodiscard]] virtual BigSegmentStoreStatus Status() const = 0;
50+
51+
/**
52+
* Listen to changes to the Big Segments store status. The handler is
53+
* invoked only when the status changes, not on every metadata poll.
54+
*
55+
* @param handler Function which will be called with the new status.
56+
* @return A IConnection which can be used to stop listening to the status.
57+
*/
58+
virtual std::unique_ptr<IConnection> OnBigSegmentStoreStatusChange(
59+
std::function<void(BigSegmentStoreStatus status)> handler) = 0;
60+
61+
virtual ~IBigSegmentStoreStatusProvider() = default;
62+
IBigSegmentStoreStatusProvider(IBigSegmentStoreStatusProvider const&) =
63+
delete;
64+
IBigSegmentStoreStatusProvider(IBigSegmentStoreStatusProvider&&) = delete;
65+
IBigSegmentStoreStatusProvider& operator=(
66+
IBigSegmentStoreStatusProvider const&) = delete;
67+
IBigSegmentStoreStatusProvider& operator=(
68+
IBigSegmentStoreStatusProvider&&) = delete;
69+
70+
protected:
71+
IBigSegmentStoreStatusProvider() = default;
72+
};
73+
74+
std::ostream& operator<<(std::ostream& out,
75+
BigSegmentStoreStatus const& status);
76+
77+
} // namespace launchdarkly::server_side

libs/server-sdk/include/launchdarkly/server_side/client.hpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <launchdarkly/value.hpp>
88

99
#include <launchdarkly/server_side/all_flags_state.hpp>
10+
#include <launchdarkly/server_side/big_segment_store_status.hpp>
1011
#include <launchdarkly/server_side/data_source_status.hpp>
1112

1213
#include <chrono>
@@ -277,10 +278,11 @@ class IClient {
277278
* @return The variation for the selected context, or default_value if the
278279
* flag is disabled in the LaunchDarkly control panel
279280
*/
280-
virtual std::string StringVariation(Context const& ctx,
281-
FlagKey const& key,
282-
std::string default_value,
283-
hooks::HookContext const& hook_context) = 0;
281+
virtual std::string StringVariation(
282+
Context const& ctx,
283+
FlagKey const& key,
284+
std::string default_value,
285+
hooks::HookContext const& hook_context) = 0;
284286

285287
/**
286288
* Returns the string value of a feature flag for a given flag key, in an
@@ -499,6 +501,14 @@ class IClient {
499501
*/
500502
virtual IDataSourceStatusProvider& DataSourceStatus() = 0;
501503

504+
/**
505+
* Returns an interface for querying the status of a Big Segment store and
506+
* subscribing to status changes. If Big Segments are not configured, the
507+
* provider reports the store as unavailable.
508+
* @return A Big Segment store status provider.
509+
*/
510+
virtual IBigSegmentStoreStatusProvider& BigSegmentStoreStatus() = 0;
511+
502512
virtual ~IClient() = default;
503513
IClient(IClient const& item) = delete;
504514
IClient(IClient&& item) = delete;
@@ -574,10 +584,11 @@ class Client : public IClient {
574584
FlagKey const& key,
575585
std::string default_value) override;
576586

577-
std::string StringVariation(Context const& ctx,
578-
FlagKey const& key,
579-
std::string default_value,
580-
hooks::HookContext const& hook_context) override;
587+
std::string StringVariation(
588+
Context const& ctx,
589+
FlagKey const& key,
590+
std::string default_value,
591+
hooks::HookContext const& hook_context) override;
581592

582593
EvaluationDetail<std::string> StringVariationDetail(
583594
Context const& ctx,
@@ -650,6 +661,8 @@ class Client : public IClient {
650661

651662
IDataSourceStatusProvider& DataSourceStatus() override;
652663

664+
IBigSegmentStoreStatusProvider& BigSegmentStoreStatus() override;
665+
653666
/**
654667
* Returns the version of the SDK.
655668
* @return String representing version of the SDK.

libs/server-sdk/include/launchdarkly/server_side/config/builders/all_builders.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <launchdarkly/server_side/config/builders/data_system/data_system_builder.hpp>
1313
#include <launchdarkly/server_side/config/builders/data_system/lazy_load_builder.hpp>
1414

15+
#include <launchdarkly/server_side/config/builders/big_segments_builder.hpp>
16+
1517
namespace launchdarkly::server_side::config::builders {
1618

1719
using SDK = launchdarkly::config::shared::ServerSDK;

libs/server-sdk/src/config/builders/big_segments_builder.hpp renamed to libs/server-sdk/include/launchdarkly/server_side/config/builders/big_segments_builder.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
#include <launchdarkly/server_side/config/built/big_segments_config.hpp>
44
#include <launchdarkly/server_side/integrations/big_segments/ibig_segment_store.hpp>
55

6+
#include <launchdarkly/error.hpp>
7+
8+
#include <tl/expected.hpp>
9+
610
#include <chrono>
711
#include <cstddef>
812
#include <memory>
@@ -68,12 +72,14 @@ class BigSegmentsBuilder {
6872
/**
6973
* @brief Resolves the configuration.
7074
*
75+
* Returns an error if the store passed to the constructor was null.
76+
*
7177
* If the configured @ref StatusPollInterval exceeds @ref StaleAfter,
7278
* the poll interval in the returned config is clamped to the
7379
* stale-after value so the SDK can detect staleness within one poll
7480
* cycle.
7581
*/
76-
[[nodiscard]] built::BigSegmentsConfig Build() const;
82+
[[nodiscard]] tl::expected<built::BigSegmentsConfig, Error> Build() const;
7783

7884
private:
7985
std::shared_ptr<integrations::IBigSegmentStore> store_;

libs/server-sdk/include/launchdarkly/server_side/config/config.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#pragma once
22

33
#include <launchdarkly/server_side/config/built/all_built.hpp>
4+
#include <launchdarkly/server_side/config/built/big_segments_config.hpp>
45
#include <launchdarkly/server_side/config/built/data_system/data_system_config.hpp>
56
#include <launchdarkly/server_side/hooks/hook.hpp>
67

78
#include <memory>
9+
#include <optional>
810
#include <vector>
911

1012
namespace launchdarkly::server_side {
@@ -17,6 +19,7 @@ struct Config {
1719
config::built::Events events,
1820
std::optional<std::string> application_tag,
1921
config::built::DataSystemConfig data_system_config,
22+
std::optional<config::built::BigSegmentsConfig> big_segments,
2023
config::built::HttpProperties http_properties,
2124
std::vector<std::shared_ptr<hooks::Hook>> hooks);
2225

@@ -31,6 +34,13 @@ struct Config {
3134

3235
config::built::DataSystemConfig const& DataSystemConfig() const;
3336

37+
/**
38+
* The Big Segments configuration, or nullopt if Big Segments were not
39+
* enabled via ConfigBuilder::BigSegments.
40+
*/
41+
[[nodiscard]] std::optional<config::built::BigSegmentsConfig> const&
42+
BigSegments() const;
43+
3444
[[nodiscard]] config::built::HttpProperties const& HttpProperties() const;
3545

3646
[[nodiscard]] config::built::Logging const& Logging() const;
@@ -46,6 +56,7 @@ struct Config {
4656
std::optional<std::string> application_tag_;
4757
config::built::Events events_;
4858
config::built::DataSystemConfig data_system_config_;
59+
std::optional<config::built::BigSegmentsConfig> big_segments_;
4960
config::built::HttpProperties http_properties_;
5061
std::vector<std::shared_ptr<hooks::Hook>> hooks_;
5162
};

libs/server-sdk/include/launchdarkly/server_side/config/config_builder.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <launchdarkly/server_side/hooks/hook.hpp>
66

77
#include <memory>
8+
#include <optional>
89
#include <vector>
910

1011
namespace launchdarkly::server_side {
@@ -50,6 +51,16 @@ class ConfigBuilder {
5051
*/
5152
config::builders::DataSystemBuilder& DataSystem();
5253

54+
/**
55+
* Configures the SDK's Big Segments behavior. Pass a BigSegmentsBuilder
56+
* constructed with the Big Segments store to use. If never called, Big
57+
* Segments are not enabled and flags referencing them evaluate as if the
58+
* context were not a member.
59+
* @param builder A configured BigSegmentsBuilder.
60+
* @return Reference to this.
61+
*/
62+
ConfigBuilder& BigSegments(config::builders::BigSegmentsBuilder builder);
63+
5364
/**
5465
* Sets the SDK's networking configuration, using an HttpPropertiesBuilder.
5566
* The builder has methods for setting individual HTTP-related properties.
@@ -99,6 +110,7 @@ class ConfigBuilder {
99110
config::builders::AppInfoBuilder app_info_builder_;
100111
config::builders::EventsBuilder events_builder_;
101112
config::builders::DataSystemBuilder data_system_builder_;
113+
std::optional<config::builders::BigSegmentsBuilder> big_segments_builder_;
102114
config::builders::HttpPropertiesBuilder http_properties_builder_;
103115
config::builders::LoggingBuilder logging_config_builder_;
104116
std::vector<std::shared_ptr<hooks::Hook>> hooks_;

libs/server-sdk/src/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ target_sources(${LIBNAME}
2222
client.cpp
2323
client_impl.cpp
2424
data_source_status.cpp
25+
big_segment_store_status.cpp
2526
instance_id.hpp
2627
instance_id.cpp
2728
config/config.cpp
@@ -49,6 +50,8 @@ target_sources(${LIBNAME}
4950
data_components/big_segments/membership_cache.cpp
5051
data_components/big_segments/big_segment_store_wrapper.hpp
5152
data_components/big_segments/big_segment_store_wrapper.cpp
53+
data_components/big_segments/big_segment_store_status_provider.hpp
54+
data_components/big_segments/big_segment_store_status_provider.cpp
5255
data_interfaces/destination/itransactional_destination.hpp
5356
data_components/memory_store/memory_store.hpp
5457
data_components/memory_store/memory_store.cpp
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <launchdarkly/server_side/big_segment_store_status.hpp>
2+
3+
namespace launchdarkly::server_side {
4+
5+
BigSegmentStoreStatus::BigSegmentStoreStatus(bool const available,
6+
bool const stale)
7+
: available_(available), stale_(stale) {}
8+
9+
bool BigSegmentStoreStatus::IsAvailable() const {
10+
return available_;
11+
}
12+
13+
bool BigSegmentStoreStatus::IsStale() const {
14+
return stale_;
15+
}
16+
17+
bool operator==(BigSegmentStoreStatus const& a,
18+
BigSegmentStoreStatus const& b) {
19+
return a.IsAvailable() == b.IsAvailable() && a.IsStale() == b.IsStale();
20+
}
21+
22+
bool operator!=(BigSegmentStoreStatus const& a,
23+
BigSegmentStoreStatus const& b) {
24+
return !(a == b);
25+
}
26+
27+
std::ostream& operator<<(std::ostream& out,
28+
BigSegmentStoreStatus const& status) {
29+
out << "BigSegmentStoreStatus(available=" << std::boolalpha
30+
<< status.IsAvailable() << ", stale=" << status.IsStale() << ")";
31+
return out;
32+
}
33+
34+
} // namespace launchdarkly::server_side

0 commit comments

Comments
 (0)