-
Notifications
You must be signed in to change notification settings - Fork 3
feat: Implement basic in-memory store and change handling. #165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
95bab24
chore: Implement architecture diagram for data store.
kinyoklion a4b2c8c
Updates types for get methods on IDataStore.
kinyoklion d368988
Update collection types.
kinyoklion 48bfc55
Merge branch 'server-side' into rlamb/data-store-arch
kinyoklion 758e825
feat: Implement basic store.
kinyoklion 6914703
Overloads for upsert and add extra const qualifiers.
kinyoklion c74f508
Merge branch 'rlamb/data-store-arch' of github.com:launchdarkly/cpp-s…
kinyoklion cd1ec6f
Merge branch 'rlamb/data-store-arch' into rlamb/basic-store-implement…
kinyoklion e82dc04
Basic data store and data source update sink interfaces.
kinyoklion 54499d6
Start adding change notifier.
kinyoklion 1df05f9
Merge branch 'server-side' into rlamb/basic-store-implementation
kinyoklion 5221784
Add empty implementation file.
kinyoklion e9734bc
Start adding memory store
kinyoklion b53b004
Basic memory store functionaliy.
kinyoklion 1d07b00
Add memory store tests.
kinyoklion 58d284f
Add tests for dependency tracker.
kinyoklion f6b9558
Split implementation and cleanup clang issues.
kinyoklion d4a2a9d
Refactoring and continue implementation of the data store updater.
kinyoklion 091bcb8
Code cleanup and more tests.
kinyoklion f3e0a5d
Add a test for using an old value after upsert.
kinyoklion 0bd9846
Merge branch 'server-side' into rlamb/basic-store-implementation
kinyoklion 2fcca51
Cleanup cmakelists.txt.
kinyoklion eedb155
Merge branch 'rlamb/basic-store-implementation' of github.com:launchd…
kinyoklion f2f4fe6
Add explainer comment for event handler.
kinyoklion 2fb9abb
Linting
kinyoklion b33ae7f
Handle added and deleted. Add new test for update, add, delete, and u…
kinyoklion e2d205a
Reformatting.
kinyoklion 0561288
Const
kinyoklion e530857
Update libs/server-sdk/src/data_store/data_store.hpp
kinyoklion e62258b
Merge branch 'rlamb/basic-store-implementation' of github.com:launchd…
kinyoklion 057355e
Aliases and descriptors
kinyoklion a3614f7
More PR feedback updates.
kinyoklion e3fadc0
Fallthrough
kinyoklion b8fb098
Remove comment.
kinyoklion fe27583
More const and lint
kinyoklion 7eb0023
Tidy
kinyoklion 5cef5f3
More kind usage.
kinyoklion 6618806
Fix namespace comment.
kinyoklion 662dbd9
Add some more const references.
kinyoklion b812099
Merge branch 'server-side' into rlamb/basic-store-implementation
kinyoklion File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
libs/server-sdk/include/launchdarkly/server_side/change_notifier.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| #pragma once | ||
|
|
||
| #include <launchdarkly/connection.hpp> | ||
|
|
||
| #include <functional> | ||
| #include <memory> | ||
| #include <set> | ||
| #include <string> | ||
|
|
||
| namespace launchdarkly::server_side { | ||
|
|
||
| /** | ||
| * Interface to allow listening for flag changes. Notification events should | ||
| * be distributed after the store has been updated. | ||
| */ | ||
| class IChangeNotifier { | ||
| public: | ||
| using ChangeSet = std::set<std::string>; | ||
| using ChangeHandler = std::function<void(std::shared_ptr<ChangeSet>)>; | ||
|
|
||
| /** | ||
| * Listen for changes to flag configuration. The change handler will be | ||
| * called with a set of affected flag keys. Changes include flags whose | ||
| * dependencies (either other flags, or segments) changed. | ||
| * | ||
| * @param signal The handler for the changes. | ||
| * @return A connection which can be used to stop listening. | ||
| */ | ||
| virtual std::unique_ptr<IConnection> OnFlagChange( | ||
| ChangeHandler handler) = 0; | ||
|
|
||
| virtual ~IChangeNotifier() = default; | ||
| IChangeNotifier(IChangeNotifier const& item) = delete; | ||
| IChangeNotifier(IChangeNotifier&& item) = delete; | ||
| IChangeNotifier& operator=(IChangeNotifier const&) = delete; | ||
| IChangeNotifier& operator=(IChangeNotifier&&) = delete; | ||
|
|
||
| protected: | ||
| IChangeNotifier() = default; | ||
| }; | ||
|
|
||
| } // namespace launchdarkly::server_side |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
31 changes: 31 additions & 0 deletions
31
libs/server-sdk/src/data_source/data_source_update_sink.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| #pragma once | ||
|
|
||
| #include <launchdarkly/data_model/flag.hpp> | ||
| #include <launchdarkly/data_model/item_descriptor.hpp> | ||
| #include <launchdarkly/data_model/sdk_data_set.hpp> | ||
| #include <launchdarkly/data_model/segment.hpp> | ||
|
|
||
| #include "../data_store/descriptors.hpp" | ||
|
|
||
| namespace launchdarkly::server_side::data_source { | ||
| /** | ||
| * Interface for handling updates from LaunchDarkly. | ||
| */ | ||
| class IDataSourceUpdateSink { | ||
| public: | ||
| virtual void Init(launchdarkly::data_model::SDKDataSet data_set) = 0; | ||
| virtual void Upsert(std::string const& key, | ||
| data_store::FlagDescriptor flag) = 0; | ||
| virtual void Upsert(std::string const& key, | ||
| data_store::SegmentDescriptor segment) = 0; | ||
|
|
||
| IDataSourceUpdateSink(IDataSourceUpdateSink const& item) = delete; | ||
| IDataSourceUpdateSink(IDataSourceUpdateSink&& item) = delete; | ||
| IDataSourceUpdateSink& operator=(IDataSourceUpdateSink const&) = delete; | ||
| IDataSourceUpdateSink& operator=(IDataSourceUpdateSink&&) = delete; | ||
| virtual ~IDataSourceUpdateSink() = default; | ||
|
|
||
| protected: | ||
| IDataSourceUpdateSink() = default; | ||
| }; | ||
| } // namespace launchdarkly::server_side::data_source |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| #pragma once | ||
|
|
||
| #include <cstddef> | ||
|
|
||
| namespace launchdarkly::server_side::data_store { | ||
| enum class DataKind : std::size_t { kFlag = 0, kSegment = 1, kKindCount = 2 }; | ||
| } // namespace launchdarkly::server_side::data_store |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| #pragma once | ||
|
|
||
| #include "descriptors.hpp" | ||
|
|
||
| #include <launchdarkly/data_model/flag.hpp> | ||
| #include <launchdarkly/data_model/item_descriptor.hpp> | ||
| #include <launchdarkly/data_model/segment.hpp> | ||
|
|
||
| #include <memory> | ||
| #include <string> | ||
| #include <unordered_map> | ||
|
|
||
| namespace launchdarkly::server_side::data_store { | ||
|
|
||
| /** | ||
| * Interface for readonly access to SDK data. | ||
| */ | ||
| class IDataStore { | ||
| public: | ||
| /** | ||
| * Get a flag from the store. | ||
| * | ||
| * @param key The key for the flag. | ||
| * @return Returns a shared_ptr to the FlagDescriptor, or a nullptr if there | ||
| * is no such flag or the flag was deleted. | ||
| */ | ||
| [[nodiscard]] virtual std::shared_ptr<FlagDescriptor> GetFlag( | ||
| std::string const& key) const = 0; | ||
|
|
||
| /** | ||
| * Get a segment from the store. | ||
| * | ||
| * @param key The key for the segment. | ||
| * @return Returns a shared_ptr to the SegmentDescriptor, or a nullptr if | ||
| * there is no such segment, or the segment was deleted. | ||
| */ | ||
| [[nodiscard]] virtual std::shared_ptr<SegmentDescriptor> GetSegment( | ||
| std::string const& key) const = 0; | ||
|
|
||
| /** | ||
| * Get all of the flags. | ||
| * | ||
| * @return Returns an unordered map of FlagDescriptors. | ||
| */ | ||
| [[nodiscard]] virtual std::unordered_map<std::string, | ||
| std::shared_ptr<FlagDescriptor>> | ||
| AllFlags() const = 0; | ||
|
|
||
| /** | ||
| * Get all of the segments. | ||
| * | ||
| * @return Returns an unordered map of SegmentDescriptors. | ||
| */ | ||
| [[nodiscard]] virtual std::unordered_map<std::string, | ||
| std::shared_ptr<SegmentDescriptor>> | ||
| AllSegments() const = 0; | ||
|
|
||
| /** | ||
| * Check if the store is initialized. | ||
| * | ||
| * @return Returns true if the store is initialized. | ||
| */ | ||
| [[nodiscard]] virtual bool Initialized() const = 0; | ||
|
|
||
| /** | ||
| * Get a description of the store. | ||
| * @return Returns a string containing a description of the store. | ||
| */ | ||
| [[nodiscard]] virtual std::string const& Description() const = 0; | ||
|
|
||
| IDataStore(IDataStore const& item) = delete; | ||
| IDataStore(IDataStore&& item) = delete; | ||
| IDataStore& operator=(IDataStore const&) = delete; | ||
| IDataStore& operator=(IDataStore&&) = delete; | ||
| virtual ~IDataStore() = default; | ||
|
|
||
| protected: | ||
| IDataStore() = default; | ||
| }; | ||
|
|
||
| } // namespace launchdarkly::server_side::data_store |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| #include "data_store_updater.hpp" | ||
|
|
||
| #include <launchdarkly/signals/boost_signal_connection.hpp> | ||
| #include <utility> | ||
|
|
||
| namespace launchdarkly::server_side::data_store { | ||
|
|
||
| std::unique_ptr<IConnection> DataStoreUpdater::OnFlagChange( | ||
| launchdarkly::server_side::IChangeNotifier::ChangeHandler handler) { | ||
| std::lock_guard lock{signal_mutex_}; | ||
|
|
||
| return std::make_unique<launchdarkly::internal::signals::SignalConnection>( | ||
| signals_.connect(handler)); | ||
| } | ||
|
|
||
| void DataStoreUpdater::Init(launchdarkly::data_model::SDKDataSet data_set) { | ||
| // Optional outside the HasListeners() scope, this allows for the changes | ||
| // to be calculated before the update and then the notification to be | ||
| // sent after the update completes. | ||
| std::optional<DependencySet> change_notifications; | ||
| if (HasListeners()) { | ||
| DependencySet updated_items; | ||
|
|
||
| CalculateChanges(DataKind::kFlag, store_->AllFlags(), data_set.flags, | ||
| updated_items); | ||
| CalculateChanges(DataKind::kSegment, store_->AllSegments(), | ||
| data_set.segments, updated_items); | ||
| change_notifications = updated_items; | ||
| } | ||
|
|
||
| dependency_tracker_.Clear(); | ||
| for (auto const& flag : data_set.flags) { | ||
| dependency_tracker_.UpdateDependencies(flag.first, flag.second); | ||
| } | ||
| for (auto const& segment : data_set.segments) { | ||
| dependency_tracker_.UpdateDependencies(segment.first, segment.second); | ||
| } | ||
| // Data will move into the store, so we want to update dependencies before | ||
| // it is moved. | ||
| sink_->Init(std::move(data_set)); | ||
| // After updating the sink, let listeners know of changes. | ||
| if (change_notifications) { | ||
| NotifyChanges(std::move(*change_notifications)); | ||
| } | ||
| } | ||
|
|
||
| void DataStoreUpdater::Upsert(std::string const& key, | ||
| data_store::FlagDescriptor flag) { | ||
| UpsertCommon(DataKind::kFlag, key, store_->GetFlag(key), std::move(flag)); | ||
| } | ||
|
|
||
| void DataStoreUpdater::Upsert(std::string const& key, | ||
| data_store::SegmentDescriptor segment) { | ||
| UpsertCommon(DataKind::kSegment, key, store_->GetSegment(key), | ||
| std::move(segment)); | ||
| } | ||
|
|
||
| bool DataStoreUpdater::HasListeners() const { | ||
| std::lock_guard lock{signal_mutex_}; | ||
| return !signals_.empty(); | ||
| } | ||
|
|
||
| void DataStoreUpdater::NotifyChanges(DependencySet changes) { | ||
| std::lock_guard lock{signal_mutex_}; | ||
| auto flag_changes = changes.SetForKind(DataKind::kFlag); | ||
| // Only emit an event if there are changes. | ||
| if (!flag_changes.empty()) { | ||
| signals_(std::make_shared<ChangeSet>(std::move(flag_changes))); | ||
| } | ||
| } | ||
|
|
||
| DataStoreUpdater::DataStoreUpdater(std::shared_ptr<IDataSourceUpdateSink> sink, | ||
| std::shared_ptr<IDataStore> store) | ||
| : sink_(std::move(sink)), store_(std::move(store)) {} | ||
|
|
||
| } // namespace launchdarkly::server_side::data_store |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.