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