-
Notifications
You must be signed in to change notification settings - Fork 2
Add Provider Repository #42
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
7 commits
Select commit
Hold shift + click to select a range
5c5309f
Add Provider Repository
NeaguGeorgiana23 bab6a76
Fix comments
NeaguGeorgiana23 f0c7590
Merge branch 'main' of https://github.com/open-feature/cpp-sdk into p…
NeaguGeorgiana23 46266ab
Fix comments
NeaguGeorgiana23 ce527a8
Fix comments
NeaguGeorgiana23 7b32130
Merge branch 'provider_repository' of https://github.com/open-feature…
NeaguGeorgiana23 0b140ad
Resolve comments
NeaguGeorgiana23 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,222 @@ | ||
| #include "openfeature/provider_repository.h" | ||
|
|
||
| #include <iostream> | ||
|
|
||
| #include "absl/status/statusor.h" | ||
| #include "openfeature/noop_provider.h" | ||
|
|
||
| namespace openfeature { | ||
|
|
||
| ProviderRepository::ProviderRepository() { | ||
| std::shared_ptr<openfeature::NoopProvider> noop_provider = | ||
| std::make_shared<NoopProvider>(); | ||
| absl::StatusOr<std::unique_ptr<FeatureProviderStatusManager>> status_manager = | ||
| FeatureProviderStatusManager::Create(noop_provider); | ||
| if (status_manager.ok()) { | ||
| std::unique_lock<std::shared_mutex> lock(repo_mutex_); | ||
| default_manager_ = std::move(status_manager.value()); | ||
| default_manager_->SetStatus(ProviderStatus::kReady); | ||
| } | ||
| } | ||
|
|
||
| ProviderRepository::~ProviderRepository() { Shutdown(); } | ||
|
|
||
| std::shared_ptr<FeatureProviderStatusManager> | ||
| ProviderRepository::GetFeatureProviderStatusManager( | ||
| std::string_view domain) const { | ||
| std::shared_lock<std::shared_mutex> lock(repo_mutex_); | ||
|
|
||
| if (domain.empty()) { | ||
| return default_manager_; | ||
| } | ||
|
|
||
| auto it = provider_manager_.find(std::string(domain)); | ||
| if (it != provider_manager_.end()) { | ||
| return it->second; | ||
| } | ||
|
|
||
| return default_manager_; | ||
| } | ||
|
|
||
| std::shared_ptr<FeatureProvider> ProviderRepository::GetProvider( | ||
| std::string_view domain) const { | ||
| std::shared_ptr<FeatureProviderStatusManager> manager = | ||
| GetFeatureProviderStatusManager(domain); | ||
|
|
||
| if (manager) { | ||
| return manager->GetProvider(); | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| void ProviderRepository::SetProvider(std::shared_ptr<FeatureProvider> provider, | ||
| const EvaluationContext& ctx, | ||
| bool wait_for_init) { | ||
| if (!provider) { | ||
| std::cerr << "Provider cannot be null" << std::endl; | ||
| return; | ||
| } | ||
| PrepareAndInitializeProvider(std::nullopt, std::move(provider), ctx, | ||
| wait_for_init); | ||
| } | ||
|
|
||
| void ProviderRepository::SetProvider(std::string_view domain, | ||
| std::shared_ptr<FeatureProvider> provider, | ||
| const EvaluationContext& ctx, | ||
| bool wait_for_init) { | ||
| if (!provider) { | ||
| std::cerr << "Provider cannot be null" << std::endl; | ||
| return; | ||
| } | ||
|
|
||
| if (domain.empty()) { | ||
| SetProvider(std::move(provider), ctx, wait_for_init); | ||
| return; | ||
| } | ||
|
|
||
| PrepareAndInitializeProvider(std::string(domain), std::move(provider), ctx, | ||
| wait_for_init); | ||
| } | ||
|
|
||
| ProviderStatus ProviderRepository::GetProviderStatus( | ||
| std::string_view domain) const { | ||
| std::shared_ptr<FeatureProviderStatusManager> manager = | ||
| GetFeatureProviderStatusManager(domain); | ||
| if (manager) { | ||
| return manager->GetStatus(); | ||
| } | ||
| return ProviderStatus::kNotReady; | ||
| } | ||
|
|
||
| void ProviderRepository::Shutdown() { | ||
| { | ||
| std::lock_guard<std::mutex> lock(threads_mutex_); | ||
| for (std::thread& thread : initialization_threads_) { | ||
| if (thread.joinable()) { | ||
| thread.join(); | ||
| } | ||
| } | ||
| initialization_threads_.clear(); | ||
| } | ||
|
|
||
| std::unique_lock<std::shared_mutex> lock(repo_mutex_); | ||
|
|
||
| if (default_manager_) { | ||
| default_manager_->Shutdown(); | ||
| default_manager_.reset(); | ||
| } | ||
|
|
||
| for (std::pair<const std::string, | ||
| std::shared_ptr<FeatureProviderStatusManager>>& pair : | ||
| provider_manager_) { | ||
| // Check if the provider is still active before shutting down. | ||
| if (pair.second->GetStatus() != ProviderStatus::kNotReady) { | ||
| pair.second->Shutdown(); | ||
| } | ||
| } | ||
| provider_manager_.clear(); | ||
| } | ||
|
|
||
| void ProviderRepository::PrepareAndInitializeProvider( | ||
| const std::optional<std::string> domain, | ||
| std::shared_ptr<FeatureProvider> new_provider, const EvaluationContext& ctx, | ||
| bool wait_for_init) { | ||
| std::shared_ptr<FeatureProviderStatusManager> new_status_manager; | ||
| std::shared_ptr<FeatureProviderStatusManager> old_status_manager; | ||
|
|
||
| { // Scoping for the unique_lock. | ||
| std::unique_lock<std::shared_mutex> lock(repo_mutex_); | ||
|
|
||
| std::shared_ptr<FeatureProviderStatusManager> existing_manager = | ||
| GetExistingStatusManagerForProvider(new_provider); | ||
| if (!existing_manager) { | ||
| absl::StatusOr<std::unique_ptr<FeatureProviderStatusManager>> manager = | ||
| FeatureProviderStatusManager::Create(new_provider); | ||
| if (!manager.ok()) { | ||
| std::cerr << "Failed to create FeatureProviderStatusManager: " | ||
| << manager.status() << std::endl; | ||
| return; | ||
| } | ||
| new_status_manager = std::move(manager.value()); | ||
| } else { | ||
| new_status_manager = existing_manager; | ||
| } | ||
|
|
||
| if (domain) { | ||
|
oxddr marked this conversation as resolved.
|
||
| // Setting a named provider. | ||
| auto it = provider_manager_.find(domain.value()); | ||
| if (it != provider_manager_.end()) { | ||
| old_status_manager = it->second; | ||
| } | ||
| provider_manager_[domain.value()] = new_status_manager; | ||
| } else { | ||
| // Setting the default provider. | ||
| old_status_manager = default_manager_; | ||
| default_manager_ = new_status_manager; | ||
| } | ||
| } // Release the lock before running initialization logic. | ||
|
|
||
| if (wait_for_init) { | ||
| InitializeProvider(new_status_manager, old_status_manager, ctx); | ||
| } else { | ||
| std::lock_guard<std::mutex> lock(threads_mutex_); | ||
| initialization_threads_.emplace_back( | ||
| [this, new_status_manager, old_status_manager, ctx] { | ||
| InitializeProvider(new_status_manager, old_status_manager, ctx); | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| void ProviderRepository::InitializeProvider( | ||
| std::shared_ptr<FeatureProviderStatusManager> new_status_manager, | ||
| std::shared_ptr<FeatureProviderStatusManager> old_status_manager, | ||
| const EvaluationContext& ctx) { | ||
| if (new_status_manager->GetStatus() == ProviderStatus::kNotReady) { | ||
| new_status_manager->Init(ctx); | ||
| } | ||
|
|
||
| if (new_status_manager->GetStatus() == ProviderStatus::kReady) { | ||
| ShutdownOldProvider(old_status_manager); | ||
| } | ||
| } | ||
|
|
||
| void ProviderRepository::ShutdownOldProvider( | ||
| std::shared_ptr<FeatureProviderStatusManager> old_status_manager) { | ||
| if (old_status_manager) { | ||
| std::shared_lock<std::shared_mutex> lock(repo_mutex_); | ||
|
|
||
| if (!IsStatusManagerRegistered(old_status_manager)) { | ||
| old_status_manager->Shutdown(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| std::shared_ptr<FeatureProviderStatusManager> | ||
| ProviderRepository::GetExistingStatusManagerForProvider( | ||
| const std::shared_ptr<FeatureProvider>& provider) { | ||
| if (default_manager_ && default_manager_->GetProvider() == provider) { | ||
| return default_manager_; | ||
| } | ||
|
|
||
| for (const auto& pair : provider_manager_) { | ||
| if (pair.second && pair.second->GetProvider() == provider) { | ||
| return pair.second; | ||
| } | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| bool ProviderRepository::IsStatusManagerRegistered( | ||
| const std::shared_ptr<FeatureProviderStatusManager>& manager) { | ||
| if (default_manager_ == manager) { | ||
| return true; | ||
| } | ||
| for (const auto& pair : provider_manager_) { | ||
| if (pair.second == manager) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| } // namespace openfeature | ||
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,96 @@ | ||
| #ifndef CPP_SDK_INCLUDE_OPENFEATURE_PROVIDER_REPOSITORY_H_ | ||
| #define CPP_SDK_INCLUDE_OPENFEATURE_PROVIDER_REPOSITORY_H_ | ||
|
|
||
| #include <memory> | ||
| #include <mutex> | ||
| #include <optional> | ||
| #include <shared_mutex> | ||
| #include <string> | ||
| #include <string_view> | ||
| #include <thread> | ||
| #include <unordered_map> | ||
| #include <vector> | ||
|
|
||
| #include "absl/status/status.h" | ||
| #include "openfeature/evaluation_context.h" | ||
| #include "openfeature/feature_provider_status_manager.h" | ||
| #include "openfeature/provider.h" | ||
| #include "openfeature/provider_status.h" | ||
|
|
||
| namespace openfeature { | ||
|
|
||
| // ProviderRepository holds a default provider and allows for registering named | ||
| // providers associated with specific domains. The repository is responsible for | ||
| // the initialization and shutdown of providers and provides thread-safe access | ||
| // to them. | ||
| class ProviderRepository { | ||
|
NeaguGeorgiana23 marked this conversation as resolved.
|
||
| public: | ||
| ProviderRepository(); | ||
| ~ProviderRepository(); | ||
|
|
||
| ProviderRepository(const ProviderRepository&) = delete; | ||
| ProviderRepository& operator=(const ProviderRepository&) = delete; | ||
|
|
||
| // If the domain is empty, fetch status manager for the default provider. | ||
| // Otherwise, fetch the status manager for a specific provider. | ||
| std::shared_ptr<FeatureProviderStatusManager> GetFeatureProviderStatusManager( | ||
| std::string_view domain = "") const; | ||
|
|
||
| // If the domain is empty then GetProvider returns the default | ||
| // FeatureProvider. Otherwise it returns the FeatureProvider for the | ||
| // domain. | ||
| std::shared_ptr<FeatureProvider> GetProvider( | ||
| std::string_view domain = "") const; | ||
|
|
||
| // Set the default provider. | ||
| void SetProvider(std::shared_ptr<FeatureProvider> provider, | ||
| const EvaluationContext& ctx, bool waitForInit); | ||
|
|
||
| // Add a provider for a domain. | ||
| void SetProvider(std::string_view domain, | ||
| std::shared_ptr<FeatureProvider> provider, | ||
| const EvaluationContext& ctx, bool waitForInit); | ||
|
|
||
| // Fetch the status of a provider for a domain. | ||
| // If the domain is not set, return the default provider status. | ||
| // If not found, return the default. | ||
| ProviderStatus GetProviderStatus(std::string_view domain = "") const; | ||
|
|
||
| // Wait for any pending initialization threads to complete before shutting | ||
| // down providers. Afterwards, shuts down all registered providers and | ||
| // clears the repository. | ||
| void Shutdown(); | ||
|
|
||
| private: | ||
| void PrepareAndInitializeProvider( | ||
| const std::optional<std::string> domain, | ||
| std::shared_ptr<FeatureProvider> new_provider, | ||
| const EvaluationContext& ctx, bool waitForInit); | ||
|
|
||
| void InitializeProvider( | ||
| std::shared_ptr<FeatureProviderStatusManager> new_status_manager, | ||
| std::shared_ptr<FeatureProviderStatusManager> old_status_manager, | ||
| const EvaluationContext& ctx); | ||
|
|
||
| void ShutdownOldProvider( | ||
| std::shared_ptr<FeatureProviderStatusManager> old_status_manager); | ||
|
|
||
| std::shared_ptr<FeatureProviderStatusManager> | ||
| GetExistingStatusManagerForProvider( | ||
| const std::shared_ptr<FeatureProvider>& provider); | ||
|
|
||
| bool IsStatusManagerRegistered( | ||
| const std::shared_ptr<FeatureProviderStatusManager>& manager); | ||
|
|
||
| std::unordered_map<std::string, std::shared_ptr<FeatureProviderStatusManager>> | ||
| provider_manager_; | ||
| std::shared_ptr<FeatureProviderStatusManager> default_manager_; | ||
| mutable std::shared_mutex repo_mutex_; | ||
|
|
||
| std::vector<std::thread> initialization_threads_; | ||
| std::mutex threads_mutex_; | ||
| }; | ||
|
|
||
| } // namespace openfeature | ||
|
|
||
| #endif // CPP_SDK_INCLUDE_OPENFEATURE_PROVIDER_REPOSITORY_H_ | ||
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
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.