C++ Server-Side SDK
LaunchDarkly SDK
Loading...
Searching...
No Matches
change_notifier.hpp
1#pragma once
2
3#include "../../data_interfaces/destination/idestination.hpp"
4#include "../../data_interfaces/store/istore.hpp"
5#include "../dependency_tracker/dependency_tracker.hpp"
6
7#include <launchdarkly/data_model/descriptors.hpp>
8#include <launchdarkly/server_side/change_notifier.hpp>
9
10#include <boost/signals2/signal.hpp>
11
12#include <memory>
13
14namespace launchdarkly::server_side::data_components {
15
17 public IChangeNotifier {
18 public:
19 template <typename Storage>
20 using Collection = data_model::SDKDataSet::Collection<std::string, Storage>;
21
22 template <typename Storage>
23 using SharedItem = std::shared_ptr<data_model::ItemDescriptor<Storage>>;
24
25 template <typename Storage>
26 using SharedCollection =
27 std::unordered_map<std::string, SharedItem<Storage>>;
28
29 ChangeNotifier(IDestination& sink, data_interfaces::IStore const& source);
30
31 std::unique_ptr<IConnection> OnFlagChange(ChangeHandler handler) override;
32
33 void Init(data_model::SDKDataSet data_set) override;
34 void Upsert(std::string const& key,
35 data_model::FlagDescriptor flag) override;
36 void Upsert(std::string const& key,
37 data_model::SegmentDescriptor segment) override;
38
39 [[nodiscard]] std::string const& Identity() const override;
40
41 ~ChangeNotifier() override = default;
42
43 ChangeNotifier(ChangeNotifier const& item) = delete;
44 ChangeNotifier(ChangeNotifier&& item) = delete;
45 ChangeNotifier& operator=(ChangeNotifier const&) = delete;
46 ChangeNotifier& operator=(ChangeNotifier&&) = delete;
47
48 private:
49 bool HasListeners() const;
50
51 template <typename FlagOrSegment>
52 void UpsertCommon(DataKind kind,
53 std::string key,
54 SharedItem<FlagOrSegment> existing,
55 data_model::ItemDescriptor<FlagOrSegment> updated) {
56 if (existing && (updated.version <= existing->version)) {
57 // Out of order update, ignore it.
58 return;
59 }
60
61 dependency_tracker_.UpdateDependencies(key, updated);
62
63 if (HasListeners()) {
64 auto updated_deps = DependencySet();
65 dependency_tracker_.CalculateChanges(kind, key, updated_deps);
66 NotifyChanges(updated_deps);
67 }
68
69 sink_.Upsert(key, updated);
70 }
71
72 template <typename FlagOrSegment>
73 void CalculateChanges(
74 DataKind kind,
75 SharedCollection<FlagOrSegment> const& existing_flags_or_segments,
76 Collection<FlagOrSegment> const& new_flags_or_segments,
77 DependencySet& updated_items) {
78 for (auto const& old_flag_or_segment : existing_flags_or_segments) {
79 auto new_flag_or_segment =
80 new_flags_or_segments.find(old_flag_or_segment.first);
81 if (new_flag_or_segment != new_flags_or_segments.end() &&
82 new_flag_or_segment->second.version <=
83 old_flag_or_segment.second->version) {
84 continue;
85 }
86
87 // Deleted.
88 dependency_tracker_.CalculateChanges(
89 kind, old_flag_or_segment.first, updated_items);
90 }
91
92 for (auto const& flag_or_segment : new_flags_or_segments) {
93 auto oldItem =
94 existing_flags_or_segments.find(flag_or_segment.first);
95 if (oldItem != existing_flags_or_segments.end() &&
96 flag_or_segment.second.version <= oldItem->second->version) {
97 continue;
98 }
99
100 // Updated or new.
101 dependency_tracker_.CalculateChanges(kind, flag_or_segment.first,
102 updated_items);
103 }
104 }
105
106 void NotifyChanges(DependencySet changes);
107
108 IDestination& sink_;
109 data_interfaces::IStore const& source_;
110
111 boost::signals2::signal<void(std::shared_ptr<ChangeSet>)> signals_;
112
113 // Recursive mutex so that has_listeners can non-conditionally lock
114 // the mutex. Otherwise, a pre-condition for the call would be holding
115 // the mutex, which is more difficult to keep consistent over the code
116 // lifetime.
117 //
118 // Signals themselves are thread-safe, and this mutex only allows us to
119 // prevent the addition of listeners between the listener check, calculation
120 // and dispatch of events.
121 mutable std::recursive_mutex signal_mutex_;
122
123 DependencyTracker dependency_tracker_;
124};
125} // namespace launchdarkly::server_side::data_components
Definition change_notifier.hpp:16
void Upsert(std::string const &key, data_model::FlagDescriptor flag) override
Upsert a flag named by key.
Definition change_notifier.cpp:47
std::string const & Identity() const override
Definition change_notifier.cpp:76
void Init(data_model::SDKDataSet data_set) override
Initialize the destination with a base set of data.
Definition change_notifier.cpp:16
std::unique_ptr< IConnection > OnFlagChange(ChangeHandler handler) override
Definition change_notifier.cpp:8
void UpdateDependencies(std::string const &key, data_model::FlagDescriptor const &flag)
Definition dependency_tracker.cpp:99
void CalculateChanges(DataKind kind, std::string const &key, DependencySet &dependency_set)
Definition dependency_tracker.cpp:130
IDestination represents a sink for data received by the SDK. A destination may be a database,...
Definition idestination.hpp:14
IStore provides shared ownership of flag and segment domain objects.
Definition istore.hpp:15