key_value_data.h 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef COMPONENTS_SQLITE_PROTO_KEY_VALUE_DATA_H_
  5. #define COMPONENTS_SQLITE_PROTO_KEY_VALUE_DATA_H_
  6. #include <algorithm>
  7. #include <map>
  8. #include <memory>
  9. #include <string>
  10. #include <unordered_map>
  11. #include <utility>
  12. #include <vector>
  13. #include "base/bind.h"
  14. #include "base/location.h"
  15. #include "base/memory/raw_ptr.h"
  16. #include "base/memory/ref_counted.h"
  17. #include "base/sequence_checker.h"
  18. #include "base/time/time.h"
  19. #include "base/timer/timer.h"
  20. #include "components/sqlite_proto/key_value_table.h"
  21. #include "components/sqlite_proto/table_manager.h"
  22. #include "third_party/abseil-cpp/absl/types/optional.h"
  23. namespace sqlite_proto {
  24. namespace internal {
  25. // FakeCompare is a dummy comparator provided so that clients using
  26. // KeyValueData<T> objects with unbounded-size caches need not
  27. // specify the Compare template parameter, which is used exclusively
  28. // for pruning the cache when it would exceed its size bound.
  29. template <typename T>
  30. struct FakeCompare {
  31. bool operator()(const T& unused_lhs, const T& unused_rhs) { return true; }
  32. };
  33. } // namespace internal
  34. // The class provides a synchronous access to the data backed by
  35. // KeyValueTable<T>. The current implementation caches all the
  36. // data in the memory. The cache size is limited by the |max_num_entries|
  37. // parameter, using the Compare function to decide which entries should
  38. // be evicted.
  39. //
  40. // NOTE: If the data store is larger than the maximum cache size, it
  41. // will be pruned on construction to satisfy the size invariant specified
  42. // by |max_num_entries|. If this is undesirable, set a sufficiently high
  43. // |max_num_entries| (or pass |max_num_entries| = absl::nullopt for
  44. // unbounded size).
  45. //
  46. // InitializeOnDBSequence() must be called on the DB sequence of the
  47. // TableManager. All other methods must be called on UI thread.
  48. template <typename T, typename Compare = internal::FakeCompare<T>>
  49. class KeyValueData {
  50. public:
  51. // Constructor. Parameters:
  52. // - |manager| provides an interface for scheduling database tasks for
  53. // execution on the database thread.
  54. // - |backend| provides the operations for updating and querying the
  55. // backing database (to be scheduled and executed using |manager|).
  56. // - |max_num_entries|, if given, caps the size of the in-memory cache;
  57. // the Compare template parameter requires a meaningful (in particular,
  58. // non-default) value iff max_num_entries is non-nullopt.
  59. // - |flush_delay| is the interval for which to gather writes and deletes
  60. // passing them through to the backing store; a value of zero will
  61. // pass writes and deletes through immediately.
  62. KeyValueData(scoped_refptr<TableManager> manager,
  63. KeyValueTable<T>* backend,
  64. absl::optional<size_t> max_num_entries,
  65. base::TimeDelta flush_delay);
  66. KeyValueData(const KeyValueData&) = delete;
  67. KeyValueData& operator=(const KeyValueData&) = delete;
  68. // Must be called on the provided TableManager's DB sequence
  69. // before calling all other methods.
  70. void InitializeOnDBSequence();
  71. // Assigns data associated with the |key| to |data|. Returns true iff the
  72. // |key| exists, false otherwise. |data| pointer may be nullptr to get the
  73. // return value only.
  74. bool TryGetData(const std::string& key, T* data) const;
  75. // Returns a view of all the cached data. (The next write or delete may
  76. // invalidate this reference.)
  77. const std::map<std::string, T>& GetAllCached() { return *data_cache_; }
  78. // Assigns data associated with the |key| to |data|.
  79. void UpdateData(const std::string& key, const T& data);
  80. // Deletes data associated with the |keys| from the database.
  81. void DeleteData(const std::vector<std::string>& keys);
  82. // Deletes all entries from the database.
  83. void DeleteAllData();
  84. private:
  85. struct EntryCompare : private Compare {
  86. bool operator()(const std::pair<std::string, T>& lhs,
  87. const std::pair<std::string, T>& rhs) {
  88. return Compare::operator()(lhs.second, rhs.second);
  89. }
  90. };
  91. enum class DeferredOperation { kUpdate, kDelete };
  92. void FlushDataToDisk();
  93. scoped_refptr<TableManager> manager_;
  94. raw_ptr<KeyValueTable<T>> backend_table_;
  95. std::unique_ptr<std::map<std::string, T>> data_cache_;
  96. std::unordered_map<std::string, DeferredOperation> deferred_updates_;
  97. base::RepeatingTimer flush_timer_;
  98. const base::TimeDelta flush_delay_;
  99. const absl::optional<size_t> max_num_entries_;
  100. EntryCompare entry_compare_;
  101. SEQUENCE_CHECKER(sequence_checker_);
  102. };
  103. template <typename T, typename Compare>
  104. KeyValueData<T, Compare>::KeyValueData(scoped_refptr<TableManager> manager,
  105. KeyValueTable<T>* backend,
  106. absl::optional<size_t> max_num_entries,
  107. base::TimeDelta flush_delay)
  108. : manager_(manager),
  109. backend_table_(backend),
  110. flush_delay_(flush_delay),
  111. max_num_entries_(max_num_entries) {}
  112. template <typename T, typename Compare>
  113. void KeyValueData<T, Compare>::InitializeOnDBSequence() {
  114. DCHECK(manager_->GetTaskRunner()->RunsTasksInCurrentSequence());
  115. auto data_map = std::make_unique<std::map<std::string, T>>();
  116. manager_->ExecuteDBTaskOnDBSequence(
  117. base::BindOnce(&KeyValueTable<T>::GetAllData, backend_table_->AsWeakPtr(),
  118. data_map.get()));
  119. // To ensure invariant that data_cache_.size() <= max_num_entries_.
  120. std::vector<std::string> keys_to_delete;
  121. while (max_num_entries_.has_value() && data_map->size() > *max_num_entries_) {
  122. auto entry_to_delete =
  123. std::min_element(data_map->begin(), data_map->end(), entry_compare_);
  124. keys_to_delete.emplace_back(entry_to_delete->first);
  125. data_map->erase(entry_to_delete);
  126. }
  127. if (!keys_to_delete.empty()) {
  128. manager_->ExecuteDBTaskOnDBSequence(base::BindOnce(
  129. &KeyValueTable<T>::DeleteData, backend_table_->AsWeakPtr(),
  130. std::vector<std::string>(keys_to_delete)));
  131. }
  132. data_cache_ = std::move(data_map);
  133. }
  134. template <typename T, typename Compare>
  135. bool KeyValueData<T, Compare>::TryGetData(const std::string& key,
  136. T* data) const {
  137. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  138. DCHECK(data_cache_);
  139. auto it = data_cache_->find(key);
  140. if (it == data_cache_->end())
  141. return false;
  142. if (data)
  143. *data = it->second;
  144. return true;
  145. }
  146. template <typename T, typename Compare>
  147. void KeyValueData<T, Compare>::UpdateData(const std::string& key,
  148. const T& data) {
  149. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  150. DCHECK(data_cache_);
  151. auto it = data_cache_->find(key);
  152. if (it == data_cache_->end()) {
  153. if (data_cache_->size() == max_num_entries_) {
  154. auto entry_to_delete = std::min_element(
  155. data_cache_->begin(), data_cache_->end(), entry_compare_);
  156. deferred_updates_[entry_to_delete->first] = DeferredOperation::kDelete;
  157. data_cache_->erase(entry_to_delete);
  158. }
  159. data_cache_->emplace(key, data);
  160. } else {
  161. it->second = data;
  162. }
  163. deferred_updates_[key] = DeferredOperation::kUpdate;
  164. if (flush_delay_.is_zero()) {
  165. // Flush immediately, only for tests.
  166. FlushDataToDisk();
  167. } else if (!flush_timer_.IsRunning()) {
  168. flush_timer_.Start(FROM_HERE, flush_delay_, this,
  169. &KeyValueData::FlushDataToDisk);
  170. }
  171. }
  172. template <typename T, typename Compare>
  173. void KeyValueData<T, Compare>::DeleteData(
  174. const std::vector<std::string>& keys) {
  175. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  176. DCHECK(data_cache_);
  177. for (const std::string& key : keys) {
  178. if (data_cache_->erase(key))
  179. deferred_updates_[key] = DeferredOperation::kDelete;
  180. }
  181. // Run all deferred updates immediately because it was requested by user.
  182. if (!deferred_updates_.empty())
  183. FlushDataToDisk();
  184. }
  185. template <typename T, typename Compare>
  186. void KeyValueData<T, Compare>::DeleteAllData() {
  187. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  188. DCHECK(data_cache_);
  189. data_cache_->clear();
  190. deferred_updates_.clear();
  191. // Delete all the content of the database immediately because it was requested
  192. // by user.
  193. manager_->ScheduleDBTask(FROM_HERE,
  194. base::BindOnce(&KeyValueTable<T>::DeleteAllData,
  195. backend_table_->AsWeakPtr()));
  196. }
  197. template <typename T, typename Compare>
  198. void KeyValueData<T, Compare>::FlushDataToDisk() {
  199. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  200. if (deferred_updates_.empty())
  201. return;
  202. std::vector<std::string> keys_to_delete;
  203. for (const auto& entry : deferred_updates_) {
  204. const std::string& key = entry.first;
  205. switch (entry.second) {
  206. case DeferredOperation::kUpdate: {
  207. auto it = data_cache_->find(key);
  208. if (it != data_cache_->end()) {
  209. manager_->ScheduleDBTask(
  210. FROM_HERE,
  211. base::BindOnce(&KeyValueTable<T>::UpdateData,
  212. backend_table_->AsWeakPtr(), key, it->second));
  213. }
  214. break;
  215. }
  216. case DeferredOperation::kDelete:
  217. keys_to_delete.push_back(key);
  218. }
  219. }
  220. if (!keys_to_delete.empty()) {
  221. manager_->ScheduleDBTask(
  222. FROM_HERE, base::BindOnce(&KeyValueTable<T>::DeleteData,
  223. backend_table_->AsWeakPtr(), keys_to_delete));
  224. }
  225. deferred_updates_.clear();
  226. }
  227. } // namespace sqlite_proto
  228. #endif // COMPONENTS_SQLITE_PROTO_KEY_VALUE_DATA_H_