123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #ifndef COMPONENTS_SQLITE_PROTO_KEY_VALUE_DATA_H_
- #define COMPONENTS_SQLITE_PROTO_KEY_VALUE_DATA_H_
- #include <algorithm>
- #include <map>
- #include <memory>
- #include <string>
- #include <unordered_map>
- #include <utility>
- #include <vector>
- #include "base/bind.h"
- #include "base/location.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/ref_counted.h"
- #include "base/sequence_checker.h"
- #include "base/time/time.h"
- #include "base/timer/timer.h"
- #include "components/sqlite_proto/key_value_table.h"
- #include "components/sqlite_proto/table_manager.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace sqlite_proto {
- namespace internal {
- // FakeCompare is a dummy comparator provided so that clients using
- // KeyValueData<T> objects with unbounded-size caches need not
- // specify the Compare template parameter, which is used exclusively
- // for pruning the cache when it would exceed its size bound.
- template <typename T>
- struct FakeCompare {
- bool operator()(const T& unused_lhs, const T& unused_rhs) { return true; }
- };
- } // namespace internal
- // The class provides a synchronous access to the data backed by
- // KeyValueTable<T>. The current implementation caches all the
- // data in the memory. The cache size is limited by the |max_num_entries|
- // parameter, using the Compare function to decide which entries should
- // be evicted.
- //
- // NOTE: If the data store is larger than the maximum cache size, it
- // will be pruned on construction to satisfy the size invariant specified
- // by |max_num_entries|. If this is undesirable, set a sufficiently high
- // |max_num_entries| (or pass |max_num_entries| = absl::nullopt for
- // unbounded size).
- //
- // InitializeOnDBSequence() must be called on the DB sequence of the
- // TableManager. All other methods must be called on UI thread.
- template <typename T, typename Compare = internal::FakeCompare<T>>
- class KeyValueData {
- public:
- // Constructor. Parameters:
- // - |manager| provides an interface for scheduling database tasks for
- // execution on the database thread.
- // - |backend| provides the operations for updating and querying the
- // backing database (to be scheduled and executed using |manager|).
- // - |max_num_entries|, if given, caps the size of the in-memory cache;
- // the Compare template parameter requires a meaningful (in particular,
- // non-default) value iff max_num_entries is non-nullopt.
- // - |flush_delay| is the interval for which to gather writes and deletes
- // passing them through to the backing store; a value of zero will
- // pass writes and deletes through immediately.
- KeyValueData(scoped_refptr<TableManager> manager,
- KeyValueTable<T>* backend,
- absl::optional<size_t> max_num_entries,
- base::TimeDelta flush_delay);
- KeyValueData(const KeyValueData&) = delete;
- KeyValueData& operator=(const KeyValueData&) = delete;
- // Must be called on the provided TableManager's DB sequence
- // before calling all other methods.
- void InitializeOnDBSequence();
- // Assigns data associated with the |key| to |data|. Returns true iff the
- // |key| exists, false otherwise. |data| pointer may be nullptr to get the
- // return value only.
- bool TryGetData(const std::string& key, T* data) const;
- // Returns a view of all the cached data. (The next write or delete may
- // invalidate this reference.)
- const std::map<std::string, T>& GetAllCached() { return *data_cache_; }
- // Assigns data associated with the |key| to |data|.
- void UpdateData(const std::string& key, const T& data);
- // Deletes data associated with the |keys| from the database.
- void DeleteData(const std::vector<std::string>& keys);
- // Deletes all entries from the database.
- void DeleteAllData();
- private:
- struct EntryCompare : private Compare {
- bool operator()(const std::pair<std::string, T>& lhs,
- const std::pair<std::string, T>& rhs) {
- return Compare::operator()(lhs.second, rhs.second);
- }
- };
- enum class DeferredOperation { kUpdate, kDelete };
- void FlushDataToDisk();
- scoped_refptr<TableManager> manager_;
- raw_ptr<KeyValueTable<T>> backend_table_;
- std::unique_ptr<std::map<std::string, T>> data_cache_;
- std::unordered_map<std::string, DeferredOperation> deferred_updates_;
- base::RepeatingTimer flush_timer_;
- const base::TimeDelta flush_delay_;
- const absl::optional<size_t> max_num_entries_;
- EntryCompare entry_compare_;
- SEQUENCE_CHECKER(sequence_checker_);
- };
- template <typename T, typename Compare>
- KeyValueData<T, Compare>::KeyValueData(scoped_refptr<TableManager> manager,
- KeyValueTable<T>* backend,
- absl::optional<size_t> max_num_entries,
- base::TimeDelta flush_delay)
- : manager_(manager),
- backend_table_(backend),
- flush_delay_(flush_delay),
- max_num_entries_(max_num_entries) {}
- template <typename T, typename Compare>
- void KeyValueData<T, Compare>::InitializeOnDBSequence() {
- DCHECK(manager_->GetTaskRunner()->RunsTasksInCurrentSequence());
- auto data_map = std::make_unique<std::map<std::string, T>>();
- manager_->ExecuteDBTaskOnDBSequence(
- base::BindOnce(&KeyValueTable<T>::GetAllData, backend_table_->AsWeakPtr(),
- data_map.get()));
- // To ensure invariant that data_cache_.size() <= max_num_entries_.
- std::vector<std::string> keys_to_delete;
- while (max_num_entries_.has_value() && data_map->size() > *max_num_entries_) {
- auto entry_to_delete =
- std::min_element(data_map->begin(), data_map->end(), entry_compare_);
- keys_to_delete.emplace_back(entry_to_delete->first);
- data_map->erase(entry_to_delete);
- }
- if (!keys_to_delete.empty()) {
- manager_->ExecuteDBTaskOnDBSequence(base::BindOnce(
- &KeyValueTable<T>::DeleteData, backend_table_->AsWeakPtr(),
- std::vector<std::string>(keys_to_delete)));
- }
- data_cache_ = std::move(data_map);
- }
- template <typename T, typename Compare>
- bool KeyValueData<T, Compare>::TryGetData(const std::string& key,
- T* data) const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(data_cache_);
- auto it = data_cache_->find(key);
- if (it == data_cache_->end())
- return false;
- if (data)
- *data = it->second;
- return true;
- }
- template <typename T, typename Compare>
- void KeyValueData<T, Compare>::UpdateData(const std::string& key,
- const T& data) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(data_cache_);
- auto it = data_cache_->find(key);
- if (it == data_cache_->end()) {
- if (data_cache_->size() == max_num_entries_) {
- auto entry_to_delete = std::min_element(
- data_cache_->begin(), data_cache_->end(), entry_compare_);
- deferred_updates_[entry_to_delete->first] = DeferredOperation::kDelete;
- data_cache_->erase(entry_to_delete);
- }
- data_cache_->emplace(key, data);
- } else {
- it->second = data;
- }
- deferred_updates_[key] = DeferredOperation::kUpdate;
- if (flush_delay_.is_zero()) {
- // Flush immediately, only for tests.
- FlushDataToDisk();
- } else if (!flush_timer_.IsRunning()) {
- flush_timer_.Start(FROM_HERE, flush_delay_, this,
- &KeyValueData::FlushDataToDisk);
- }
- }
- template <typename T, typename Compare>
- void KeyValueData<T, Compare>::DeleteData(
- const std::vector<std::string>& keys) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(data_cache_);
- for (const std::string& key : keys) {
- if (data_cache_->erase(key))
- deferred_updates_[key] = DeferredOperation::kDelete;
- }
- // Run all deferred updates immediately because it was requested by user.
- if (!deferred_updates_.empty())
- FlushDataToDisk();
- }
- template <typename T, typename Compare>
- void KeyValueData<T, Compare>::DeleteAllData() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(data_cache_);
- data_cache_->clear();
- deferred_updates_.clear();
- // Delete all the content of the database immediately because it was requested
- // by user.
- manager_->ScheduleDBTask(FROM_HERE,
- base::BindOnce(&KeyValueTable<T>::DeleteAllData,
- backend_table_->AsWeakPtr()));
- }
- template <typename T, typename Compare>
- void KeyValueData<T, Compare>::FlushDataToDisk() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (deferred_updates_.empty())
- return;
- std::vector<std::string> keys_to_delete;
- for (const auto& entry : deferred_updates_) {
- const std::string& key = entry.first;
- switch (entry.second) {
- case DeferredOperation::kUpdate: {
- auto it = data_cache_->find(key);
- if (it != data_cache_->end()) {
- manager_->ScheduleDBTask(
- FROM_HERE,
- base::BindOnce(&KeyValueTable<T>::UpdateData,
- backend_table_->AsWeakPtr(), key, it->second));
- }
- break;
- }
- case DeferredOperation::kDelete:
- keys_to_delete.push_back(key);
- }
- }
- if (!keys_to_delete.empty()) {
- manager_->ScheduleDBTask(
- FROM_HERE, base::BindOnce(&KeyValueTable<T>::DeleteData,
- backend_table_->AsWeakPtr(), keys_to_delete));
- }
- deferred_updates_.clear();
- }
- } // namespace sqlite_proto
- #endif // COMPONENTS_SQLITE_PROTO_KEY_VALUE_DATA_H_
|