1 // Copyright 2018 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 
5 #include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
6 
7 #include <string>
8 
9 #include "base/sequenced_task_runner.h"
10 #include "base/task/post_task.h"
11 #include "base/task/task_traits.h"
12 #include "base/threading/sequenced_task_runner_handle.h"
13 #include "components/leveldb_proto/internal/leveldb_database.h"
14 #include "components/leveldb_proto/internal/proto_leveldb_wrapper_metrics.h"
15 #include "components/leveldb_proto/public/proto_database.h"
16 
17 namespace leveldb_proto {
18 
19 namespace {
20 
InitFromTaskRunner(LevelDB * database,const base::FilePath & database_dir,const leveldb_env::Options & options,bool destroy_on_corruption,const std::string & client_id)21 Enums::InitStatus InitFromTaskRunner(LevelDB* database,
22                                      const base::FilePath& database_dir,
23                                      const leveldb_env::Options& options,
24                                      bool destroy_on_corruption,
25                                      const std::string& client_id) {
26   // TODO(cjhopman): Histogram for database size.
27   auto status = database->Init(database_dir, options, destroy_on_corruption);
28   ProtoLevelDBWrapperMetrics::RecordInit(client_id, status);
29 
30   if (status.ok())
31     return Enums::InitStatus::kOK;
32   if (status.IsCorruption())
33     return Enums::InitStatus::kCorrupt;
34   if (status.IsNotSupportedError() || status.IsInvalidArgument())
35     return Enums::InitStatus::kInvalidOperation;
36   return Enums::InitStatus::kError;
37 }
38 
DestroyFromTaskRunner(LevelDB * database,const std::string & client_id)39 bool DestroyFromTaskRunner(LevelDB* database, const std::string& client_id) {
40   auto status = database->Destroy();
41   bool success = status.ok();
42   ProtoLevelDBWrapperMetrics::RecordDestroy(client_id, success);
43 
44   return success;
45 }
46 
DestroyWithDirectoryFromTaskRunner(const base::FilePath & db_dir,const std::string & client_id)47 bool DestroyWithDirectoryFromTaskRunner(const base::FilePath& db_dir,
48                                         const std::string& client_id) {
49   leveldb::Status result = leveldb::DestroyDB(
50       db_dir.AsUTF8Unsafe(), leveldb_proto::CreateSimpleOptions());
51   bool success = result.ok();
52 
53   if (!client_id.empty())
54     ProtoLevelDBWrapperMetrics::RecordDestroy(client_id, success);
55 
56   return success;
57 }
58 
LoadKeysFromTaskRunner(LevelDB * database,const std::string & target_prefix,const std::string & client_id,Callbacks::LoadKeysCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner)59 void LoadKeysFromTaskRunner(
60     LevelDB* database,
61     const std::string& target_prefix,
62     const std::string& client_id,
63     Callbacks::LoadKeysCallback callback,
64     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
65   auto keys = std::make_unique<KeyVector>();
66   bool success = database->LoadKeys(target_prefix, keys.get());
67   ProtoLevelDBWrapperMetrics::RecordLoadKeys(client_id, success);
68   callback_task_runner->PostTask(
69       FROM_HERE, base::BindOnce(std::move(callback), success, std::move(keys)));
70 }
71 
RemoveKeysFromTaskRunner(LevelDB * database,const std::string & target_prefix,const KeyFilter & filter,const std::string & client_id,Callbacks::UpdateCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner)72 void RemoveKeysFromTaskRunner(
73     LevelDB* database,
74     const std::string& target_prefix,
75     const KeyFilter& filter,
76     const std::string& client_id,
77     Callbacks::UpdateCallback callback,
78     scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
79   leveldb::Status status;
80   bool success = database->UpdateWithRemoveFilter(base::StringPairs(), filter,
81                                                   target_prefix, &status);
82   ProtoLevelDBWrapperMetrics::RecordUpdate(client_id, success, status);
83   callback_task_runner->PostTask(FROM_HERE,
84                                  base::BindOnce(std::move(callback), success));
85 }
86 
RunLoadCallback(Callbacks::LoadCallback callback,bool * success,std::unique_ptr<ValueVector> entries)87 void RunLoadCallback(Callbacks::LoadCallback callback,
88                      bool* success,
89                      std::unique_ptr<ValueVector> entries) {
90   std::move(callback).Run(*success, std::move(entries));
91 }
92 
RunLoadKeysAndEntriesCallback(Callbacks::LoadKeysAndEntriesCallback callback,bool * success,std::unique_ptr<KeyValueMap> keys_entries)93 void RunLoadKeysAndEntriesCallback(
94     Callbacks::LoadKeysAndEntriesCallback callback,
95     bool* success,
96     std::unique_ptr<KeyValueMap> keys_entries) {
97   std::move(callback).Run(*success, std::move(keys_entries));
98 }
99 
RunGetCallback(Callbacks::GetCallback callback,const bool * success,const bool * found,std::unique_ptr<std::string> entry)100 void RunGetCallback(Callbacks::GetCallback callback,
101                     const bool* success,
102                     const bool* found,
103                     std::unique_ptr<std::string> entry) {
104   std::move(callback).Run(*success, *found ? std::move(entry) : nullptr);
105 }
106 
UpdateEntriesFromTaskRunner(LevelDB * database,std::unique_ptr<KeyValueVector> entries_to_save,std::unique_ptr<KeyVector> keys_to_remove,const std::string & client_id)107 bool UpdateEntriesFromTaskRunner(
108     LevelDB* database,
109     std::unique_ptr<KeyValueVector> entries_to_save,
110     std::unique_ptr<KeyVector> keys_to_remove,
111     const std::string& client_id) {
112   DCHECK(entries_to_save);
113   DCHECK(keys_to_remove);
114   leveldb::Status status;
115   bool success = database->Save(*entries_to_save, *keys_to_remove, &status);
116   ProtoLevelDBWrapperMetrics::RecordUpdate(client_id, success, status);
117   return success;
118 }
119 
UpdateEntriesWithRemoveFilterFromTaskRunner(LevelDB * database,std::unique_ptr<KeyValueVector> entries_to_save,const KeyFilter & delete_key_filter,const std::string & target_prefix,const std::string & client_id)120 bool UpdateEntriesWithRemoveFilterFromTaskRunner(
121     LevelDB* database,
122     std::unique_ptr<KeyValueVector> entries_to_save,
123     const KeyFilter& delete_key_filter,
124     const std::string& target_prefix,
125     const std::string& client_id) {
126   DCHECK(entries_to_save);
127   leveldb::Status status;
128   bool success = database->UpdateWithRemoveFilter(
129       *entries_to_save, delete_key_filter, target_prefix, &status);
130   ProtoLevelDBWrapperMetrics::RecordUpdate(client_id, success, status);
131   return success;
132 }
133 
LoadKeysAndEntriesFromTaskRunner(LevelDB * database,const KeyFilter & while_callback,const KeyFilter & filter,const leveldb::ReadOptions & options,const std::string & target_prefix,const std::string & client_id,bool * success,KeyValueMap * keys_entries)134 void LoadKeysAndEntriesFromTaskRunner(LevelDB* database,
135                                       const KeyFilter& while_callback,
136                                       const KeyFilter& filter,
137                                       const leveldb::ReadOptions& options,
138                                       const std::string& target_prefix,
139                                       const std::string& client_id,
140                                       bool* success,
141                                       KeyValueMap* keys_entries) {
142   DCHECK(success);
143   DCHECK(keys_entries);
144   keys_entries->clear();
145 
146   *success = database->LoadKeysAndEntriesWhile(filter, keys_entries, options,
147                                                target_prefix, while_callback);
148 
149   ProtoLevelDBWrapperMetrics::RecordLoadKeysAndEntries(client_id, success);
150 }
151 
LoadEntriesFromTaskRunner(LevelDB * database,const KeyFilter & filter,const leveldb::ReadOptions & options,const std::string & target_prefix,const std::string & client_id,bool * success,ValueVector * entries)152 void LoadEntriesFromTaskRunner(LevelDB* database,
153                                const KeyFilter& filter,
154                                const leveldb::ReadOptions& options,
155                                const std::string& target_prefix,
156                                const std::string& client_id,
157                                bool* success,
158                                ValueVector* entries) {
159   *success = database->LoadWithFilter(filter, entries, options, target_prefix);
160 
161   ProtoLevelDBWrapperMetrics::RecordLoadEntries(client_id, success);
162 }
163 
GetEntryFromTaskRunner(LevelDB * database,const std::string & key,const std::string & client_id,bool * success,bool * found,std::string * entry)164 void GetEntryFromTaskRunner(LevelDB* database,
165                             const std::string& key,
166                             const std::string& client_id,
167                             bool* success,
168                             bool* found,
169                             std::string* entry) {
170   leveldb::Status status;
171   *success = database->Get(key, found, entry, &status);
172 
173   ProtoLevelDBWrapperMetrics::RecordGet(client_id, *success, *found, status);
174 }
175 }  // namespace
176 
ProtoLevelDBWrapper(const scoped_refptr<base::SequencedTaskRunner> & task_runner)177 ProtoLevelDBWrapper::ProtoLevelDBWrapper(
178     const scoped_refptr<base::SequencedTaskRunner>& task_runner)
179     : task_runner_(task_runner) {
180   DETACH_FROM_SEQUENCE(sequence_checker_);
181 }
182 
ProtoLevelDBWrapper(const scoped_refptr<base::SequencedTaskRunner> & task_runner,LevelDB * db)183 ProtoLevelDBWrapper::ProtoLevelDBWrapper(
184     const scoped_refptr<base::SequencedTaskRunner>& task_runner,
185     LevelDB* db)
186     : task_runner_(task_runner), db_(db) {
187   DETACH_FROM_SEQUENCE(sequence_checker_);
188 }
189 
190 ProtoLevelDBWrapper::~ProtoLevelDBWrapper() = default;
191 
RunInitCallback(Callbacks::InitCallback callback,const leveldb::Status * status)192 void ProtoLevelDBWrapper::RunInitCallback(Callbacks::InitCallback callback,
193                                           const leveldb::Status* status) {
194   std::move(callback).Run(status->ok());
195 }
196 
InitWithDatabase(LevelDB * database,const base::FilePath & database_dir,const leveldb_env::Options & options,bool destroy_on_corruption,Callbacks::InitStatusCallback callback)197 void ProtoLevelDBWrapper::InitWithDatabase(
198     LevelDB* database,
199     const base::FilePath& database_dir,
200     const leveldb_env::Options& options,
201     bool destroy_on_corruption,
202     Callbacks::InitStatusCallback callback) {
203   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
204   DCHECK(database);
205   db_ = database;
206 
207   base::PostTaskAndReplyWithResult(
208       task_runner_.get(), FROM_HERE,
209       base::BindOnce(InitFromTaskRunner, base::Unretained(db_), database_dir,
210                      options, destroy_on_corruption, metrics_id_),
211       std::move(callback));
212 }
213 
UpdateEntries(std::unique_ptr<KeyValueVector> entries_to_save,std::unique_ptr<KeyVector> keys_to_remove,typename Callbacks::UpdateCallback callback)214 void ProtoLevelDBWrapper::UpdateEntries(
215     std::unique_ptr<KeyValueVector> entries_to_save,
216     std::unique_ptr<KeyVector> keys_to_remove,
217     typename Callbacks::UpdateCallback callback) {
218   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
219   base::PostTaskAndReplyWithResult(
220       task_runner_.get(), FROM_HERE,
221       base::BindOnce(UpdateEntriesFromTaskRunner, base::Unretained(db_),
222                      std::move(entries_to_save), std::move(keys_to_remove),
223                      metrics_id_),
224       std::move(callback));
225 }
226 
UpdateEntriesWithRemoveFilter(std::unique_ptr<KeyValueVector> entries_to_save,const KeyFilter & delete_key_filter,Callbacks::UpdateCallback callback)227 void ProtoLevelDBWrapper::UpdateEntriesWithRemoveFilter(
228     std::unique_ptr<KeyValueVector> entries_to_save,
229     const KeyFilter& delete_key_filter,
230     Callbacks::UpdateCallback callback) {
231   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
232   UpdateEntriesWithRemoveFilter(std::move(entries_to_save), delete_key_filter,
233                                 std::string(), std::move(callback));
234 }
235 
UpdateEntriesWithRemoveFilter(std::unique_ptr<KeyValueVector> entries_to_save,const KeyFilter & delete_key_filter,const std::string & target_prefix,Callbacks::UpdateCallback callback)236 void ProtoLevelDBWrapper::UpdateEntriesWithRemoveFilter(
237     std::unique_ptr<KeyValueVector> entries_to_save,
238     const KeyFilter& delete_key_filter,
239     const std::string& target_prefix,
240     Callbacks::UpdateCallback callback) {
241   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
242   base::PostTaskAndReplyWithResult(
243       task_runner_.get(), FROM_HERE,
244       base::BindOnce(UpdateEntriesWithRemoveFilterFromTaskRunner,
245                      base::Unretained(db_), std::move(entries_to_save),
246                      delete_key_filter, target_prefix, metrics_id_),
247       std::move(callback));
248 }
249 
LoadEntries(Callbacks::LoadCallback callback)250 void ProtoLevelDBWrapper::LoadEntries(Callbacks::LoadCallback callback) {
251   LoadEntriesWithFilter(KeyFilter(), std::move(callback));
252 }
253 
LoadEntriesWithFilter(const KeyFilter & key_filter,Callbacks::LoadCallback callback)254 void ProtoLevelDBWrapper::LoadEntriesWithFilter(
255     const KeyFilter& key_filter,
256     Callbacks::LoadCallback callback) {
257   LoadEntriesWithFilter(key_filter, leveldb::ReadOptions(), std::string(),
258                         std::move(callback));
259 }
260 
LoadEntriesWithFilter(const KeyFilter & key_filter,const leveldb::ReadOptions & options,const std::string & target_prefix,Callbacks::LoadCallback callback)261 void ProtoLevelDBWrapper::LoadEntriesWithFilter(
262     const KeyFilter& key_filter,
263     const leveldb::ReadOptions& options,
264     const std::string& target_prefix,
265     Callbacks::LoadCallback callback) {
266   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
267   bool* success = new bool(false);
268   auto entries = std::make_unique<ValueVector>();
269   // Get this pointer before |entries| is std::move()'d so we can use it below.
270   auto* entries_ptr = entries.get();
271 
272   task_runner_->PostTaskAndReply(
273       FROM_HERE,
274       base::BindOnce(LoadEntriesFromTaskRunner, base::Unretained(db_),
275                      key_filter, options, target_prefix, metrics_id_, success,
276                      entries_ptr),
277       base::BindOnce(RunLoadCallback, std::move(callback), base::Owned(success),
278                      std::move(entries)));
279 }
280 
LoadKeysAndEntries(Callbacks::LoadKeysAndEntriesCallback callback)281 void ProtoLevelDBWrapper::LoadKeysAndEntries(
282     Callbacks::LoadKeysAndEntriesCallback callback) {
283   LoadKeysAndEntriesWithFilter(KeyFilter(), std::move(callback));
284 }
285 
LoadKeysAndEntriesWithFilter(const KeyFilter & key_filter,Callbacks::LoadKeysAndEntriesCallback callback)286 void ProtoLevelDBWrapper::LoadKeysAndEntriesWithFilter(
287     const KeyFilter& key_filter,
288     Callbacks::LoadKeysAndEntriesCallback callback) {
289   LoadKeysAndEntriesWithFilter(key_filter, leveldb::ReadOptions(),
290                                std::string(), std::move(callback));
291 }
292 
LoadKeysAndEntriesWithFilter(const KeyFilter & key_filter,const leveldb::ReadOptions & options,const std::string & target_prefix,Callbacks::LoadKeysAndEntriesCallback callback)293 void ProtoLevelDBWrapper::LoadKeysAndEntriesWithFilter(
294     const KeyFilter& key_filter,
295     const leveldb::ReadOptions& options,
296     const std::string& target_prefix,
297     Callbacks::LoadKeysAndEntriesCallback callback) {
298   LoadKeysAndEntriesWhile(
299       base::BindRepeating(
300           [](const std::string& prefix, const std::string& key) {
301             return base::StartsWith(key, prefix, base::CompareCase::SENSITIVE);
302           },
303           target_prefix),
304       key_filter, options, target_prefix, std::move(callback));
305 }
306 
LoadKeysAndEntriesInRange(const std::string & start,const std::string & end,Callbacks::LoadKeysAndEntriesCallback callback)307 void ProtoLevelDBWrapper::LoadKeysAndEntriesInRange(
308     const std::string& start,
309     const std::string& end,
310     Callbacks::LoadKeysAndEntriesCallback callback) {
311   LoadKeysAndEntriesWhile(
312       base::BindRepeating(
313           [](const std::string& range_end, const std::string& key) {
314             return key.compare(range_end) <= 0;
315           },
316           end),
317       KeyFilter(), leveldb::ReadOptions(), start, std::move(callback));
318 }
319 
LoadKeysAndEntriesWhile(const KeyFilter & while_callback,const KeyFilter & filter,const leveldb::ReadOptions & options,const std::string & target_prefix,Callbacks::LoadKeysAndEntriesCallback callback)320 void ProtoLevelDBWrapper::LoadKeysAndEntriesWhile(
321     const KeyFilter& while_callback,
322     const KeyFilter& filter,
323     const leveldb::ReadOptions& options,
324     const std::string& target_prefix,
325     Callbacks::LoadKeysAndEntriesCallback callback) {
326   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
327   bool* success = new bool(false);
328   auto keys_entries = std::make_unique<KeyValueMap>();
329   // Get this pointer before |keys_entries| is std::move()'d so we can use it
330   // below.
331   auto* keys_entries_ptr = keys_entries.get();
332   task_runner_->PostTaskAndReply(
333       FROM_HERE,
334       base::BindOnce(LoadKeysAndEntriesFromTaskRunner, base::Unretained(db_),
335                      while_callback, filter, options, target_prefix,
336                      metrics_id_, success, keys_entries_ptr),
337       base::BindOnce(RunLoadKeysAndEntriesCallback, std::move(callback),
338                      base::Owned(success), std::move(keys_entries)));
339 }
340 
GetEntry(const std::string & key,Callbacks::GetCallback callback)341 void ProtoLevelDBWrapper::GetEntry(const std::string& key,
342                                    Callbacks::GetCallback callback) {
343   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
344   bool* success = new bool(false);
345   bool* found = new bool(false);
346   auto entry = std::make_unique<std::string>();
347   // Get this pointer before |entry| is std::move()'d so we can use it below.
348   auto* entry_ptr = entry.get();
349 
350   task_runner_->PostTaskAndReply(
351       FROM_HERE,
352       base::BindOnce(GetEntryFromTaskRunner, base::Unretained(db_), key,
353                      metrics_id_, success, found, entry_ptr),
354       base::BindOnce(RunGetCallback, std::move(callback), base::Owned(success),
355                      base::Owned(found), std::move(entry)));
356 }
357 
LoadKeys(typename Callbacks::LoadKeysCallback callback)358 void ProtoLevelDBWrapper::LoadKeys(
359     typename Callbacks::LoadKeysCallback callback) {
360   LoadKeys(std::string(), std::move(callback));
361 }
362 
LoadKeys(const std::string & target_prefix,typename Callbacks::LoadKeysCallback callback)363 void ProtoLevelDBWrapper::LoadKeys(
364     const std::string& target_prefix,
365     typename Callbacks::LoadKeysCallback callback) {
366   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
367   task_runner_->PostTask(
368       FROM_HERE, base::BindOnce(LoadKeysFromTaskRunner, base::Unretained(db_),
369                                 target_prefix, metrics_id_, std::move(callback),
370                                 base::SequencedTaskRunnerHandle::Get()));
371 }
372 
RemoveKeys(const KeyFilter & filter,const std::string & target_prefix,Callbacks::UpdateCallback callback)373 void ProtoLevelDBWrapper::RemoveKeys(const KeyFilter& filter,
374                                      const std::string& target_prefix,
375                                      Callbacks::UpdateCallback callback) {
376   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
377   task_runner_->PostTask(
378       FROM_HERE,
379       base::BindOnce(RemoveKeysFromTaskRunner, base::Unretained(db_),
380                      target_prefix, filter, metrics_id_, std::move(callback),
381                      base::SequencedTaskRunnerHandle::Get()));
382 }
383 
Destroy(Callbacks::DestroyCallback callback)384 void ProtoLevelDBWrapper::Destroy(Callbacks::DestroyCallback callback) {
385   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
386   DCHECK(db_);
387 
388   base::PostTaskAndReplyWithResult(
389       task_runner_.get(), FROM_HERE,
390       base::BindOnce(DestroyFromTaskRunner, base::Unretained(db_), metrics_id_),
391       std::move(callback));
392 }
393 
Destroy(const base::FilePath & db_dir,const std::string & client_id,const scoped_refptr<base::SequencedTaskRunner> & task_runner,Callbacks::DestroyCallback callback)394 void ProtoLevelDBWrapper::Destroy(
395     const base::FilePath& db_dir,
396     const std::string& client_id,
397     const scoped_refptr<base::SequencedTaskRunner>& task_runner,
398     Callbacks::DestroyCallback callback) {
399   base::PostTaskAndReplyWithResult(
400       task_runner.get(), FROM_HERE,
401       base::BindOnce(DestroyWithDirectoryFromTaskRunner, db_dir, client_id),
402       std::move(callback));
403 }
404 
SetMetricsId(const std::string & id)405 void ProtoLevelDBWrapper::SetMetricsId(const std::string& id) {
406   metrics_id_ = id;
407 }
408 
GetApproximateMemoryUse(uint64_t * approx_mem_use)409 bool ProtoLevelDBWrapper::GetApproximateMemoryUse(uint64_t* approx_mem_use) {
410   if (!db_)
411     return 0;
412 
413   return db_->GetApproximateMemoryUse(approx_mem_use);
414 }
415 
416 const scoped_refptr<base::SequencedTaskRunner>&
task_runner()417 ProtoLevelDBWrapper::task_runner() {
418   return task_runner_;
419 }
420 
421 }  // namespace leveldb_proto
422