C++ Server-Side SDK
LaunchDarkly SDK
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 
14 namespace 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