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/shared_proto_database.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/callback_helpers.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/task/post_task.h"
14 #include "base/task/thread_pool.h"
15 #include "components/leveldb_proto/internal/leveldb_database.h"
16 #include "components/leveldb_proto/internal/proto_database_selector.h"
17 #include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
18 #include "components/leveldb_proto/public/proto_database.h"
19 #include "components/leveldb_proto/public/proto_database_provider.h"
20 #include "components/leveldb_proto/public/shared_proto_database_client_list.h"
21
22 namespace leveldb_proto {
23
24 namespace {
25
26 const base::FilePath::CharType kMetadataDatabasePath[] =
27 FILE_PATH_LITERAL("metadata");
28 const int kMaxInitMetaDatabaseAttempts = 3;
29
30 const char kGlobalMetadataKey[] = "__global";
31
32 const char kSharedProtoDatabaseUmaName[] = "SharedDb";
33
34 } // namespace
35
RunInitStatusCallbackOnCallingSequence(SharedProtoDatabase::SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner,Enums::InitStatus status,SharedDBMetadataProto::MigrationStatus migration_status,ProtoDatabaseSelector::ProtoDatabaseInitState metric)36 inline void RunInitStatusCallbackOnCallingSequence(
37 SharedProtoDatabase::SharedClientInitCallback callback,
38 scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
39 Enums::InitStatus status,
40 SharedDBMetadataProto::MigrationStatus migration_status,
41 ProtoDatabaseSelector::ProtoDatabaseInitState metric) {
42 ProtoDatabaseSelector::RecordInitState(metric);
43 callback_task_runner->PostTask(
44 FROM_HERE, base::BindOnce(std::move(callback), status, migration_status));
45 }
46
InitRequest(SharedClientInitCallback callback,const scoped_refptr<base::SequencedTaskRunner> & task_runner,const std::string & client_db_id)47 SharedProtoDatabase::InitRequest::InitRequest(
48 SharedClientInitCallback callback,
49 const scoped_refptr<base::SequencedTaskRunner>& task_runner,
50 const std::string& client_db_id)
51 : callback(std::move(callback)),
52 task_runner(std::move(task_runner)),
53 client_db_id(client_db_id) {}
54
55 SharedProtoDatabase::InitRequest::~InitRequest() = default;
56
SharedProtoDatabase(const std::string & client_db_id,const base::FilePath & db_dir)57 SharedProtoDatabase::SharedProtoDatabase(const std::string& client_db_id,
58 const base::FilePath& db_dir)
59 : task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
60 {base::MayBlock(),
61 // crbug/1006954 and crbug/976223 explain why one of the clients
62 // needs run in visible priority. Download DB is always loaded to
63 // check for in progress downloads at startup. So, always load shared
64 // db in USER_VISIBLE priority.
65 base::TaskPriority::USER_VISIBLE,
66 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
67 db_dir_(db_dir),
68 db_(std::make_unique<LevelDB>(client_db_id.c_str())),
69 db_wrapper_(std::make_unique<ProtoLevelDBWrapper>(task_runner_)),
70 metadata_db_wrapper_(
71 ProtoDatabaseProvider::GetUniqueDB<SharedDBMetadataProto>(
72 ProtoDbType::SHARED_DB_METADATA,
73 db_dir_.Append(base::FilePath(kMetadataDatabasePath)),
74 task_runner_)) {
75 DETACH_FROM_SEQUENCE(on_task_runner_);
76 }
77
78 // All init functionality runs on the same SequencedTaskRunner, so any caller of
79 // this after a database Init will receive the correct status of the database.
80 // PostTaskAndReply is used to ensure that we call the Init callback on its
81 // original calling thread.
GetDatabaseInitStatusAsync(const std::string & client_db_id,Callbacks::InitStatusCallback callback)82 void SharedProtoDatabase::GetDatabaseInitStatusAsync(
83 const std::string& client_db_id,
84 Callbacks::InitStatusCallback callback) {
85 DCHECK(base::SequencedTaskRunnerHandle::IsSet());
86 task_runner_->PostTask(
87 FROM_HERE, base::BindOnce(&SharedProtoDatabase::RunInitCallback, this,
88 std::move(callback),
89 base::SequencedTaskRunnerHandle::Get()));
90 }
91
RunInitCallback(Callbacks::InitStatusCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner)92 void SharedProtoDatabase::RunInitCallback(
93 Callbacks::InitStatusCallback callback,
94 scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
95 callback_task_runner->PostTask(
96 FROM_HERE, base::BindOnce(std::move(callback), init_status_));
97 }
98
UpdateClientMetadataAsync(const std::string & client_db_id,SharedDBMetadataProto::MigrationStatus migration_status,base::OnceCallback<void (bool)> callback)99 void SharedProtoDatabase::UpdateClientMetadataAsync(
100 const std::string& client_db_id,
101 SharedDBMetadataProto::MigrationStatus migration_status,
102 base::OnceCallback<void(bool)> callback) {
103 if (base::SequencedTaskRunnerHandle::Get() != task_runner_) {
104 task_runner_->PostTask(
105 FROM_HERE,
106 base::BindOnce(&SharedProtoDatabase::UpdateClientMetadataAsync, this,
107 client_db_id, migration_status, std::move(callback)));
108 return;
109 }
110 auto update_entries = std::make_unique<
111 std::vector<std::pair<std::string, SharedDBMetadataProto>>>();
112 SharedDBMetadataProto write_proto;
113 write_proto.set_corruptions(metadata_->corruptions());
114 write_proto.set_migration_status(migration_status);
115 update_entries->emplace_back(
116 std::make_pair(std::string(client_db_id), write_proto));
117
118 metadata_db_wrapper_->UpdateEntries(
119 std::move(update_entries), std::make_unique<std::vector<std::string>>(),
120 std::move(callback));
121 }
122
GetClientMetadataAsync(const std::string & client_db_id,SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner)123 void SharedProtoDatabase::GetClientMetadataAsync(
124 const std::string& client_db_id,
125 SharedClientInitCallback callback,
126 scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
127 // |metadata_db_wrapper_| uses the same TaskRunner as Init and the main
128 // DB, so making this call directly here without PostTasking is safe. In
129 // addition, GetEntry uses PostTaskAndReply so the callback will be triggered
130 // on the calling sequence.
131 metadata_db_wrapper_->GetEntry(
132 std::string(client_db_id),
133 base::BindOnce(&SharedProtoDatabase::OnGetClientMetadata, this,
134 client_db_id, std::move(callback),
135 std::move(callback_task_runner)));
136 }
137
138 // As mentioned above, |current_task_runner| is the appropriate calling sequence
139 // for the callback since the GetEntry call in GetClientMetadataAsync uses
140 // PostTaskAndReply.
OnGetClientMetadata(const std::string & client_db_id,SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner,bool success,std::unique_ptr<SharedDBMetadataProto> proto)141 void SharedProtoDatabase::OnGetClientMetadata(
142 const std::string& client_db_id,
143 SharedClientInitCallback callback,
144 scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
145 bool success,
146 std::unique_ptr<SharedDBMetadataProto> proto) {
147 // If fetching metadata failed, then ignore the error.
148 if (!success) {
149 RunInitStatusCallbackOnCallingSequence(
150 std::move(callback), std::move(callback_task_runner),
151 Enums::InitStatus::kOK, SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
152 ProtoDatabaseSelector::ProtoDatabaseInitState::
153 kSharedDbMetadataLoadFailed);
154 return;
155 }
156 if (!proto || !proto->has_migration_status()) {
157 UpdateClientMetadataAsync(
158 client_db_id, SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
159 base::BindOnce(
160 [](SharedClientInitCallback callback,
161 scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
162 bool update_success) {
163 // Do not care about update success since next time we will reset
164 // corruption and migration status to 0.
165 RunInitStatusCallbackOnCallingSequence(
166 std::move(callback), std::move(callback_task_runner),
167 Enums::InitStatus::kOK,
168 SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
169 ProtoDatabaseSelector::ProtoDatabaseInitState::
170 kSharedDbMetadataWriteFailed);
171 },
172 std::move(callback), std::move(callback_task_runner)));
173 return;
174 }
175 // If we've made it here, we know that the current status of our database is
176 // OK. Make it return corrupt if the metadata disagrees.
177 bool is_corrupt = metadata_->corruptions() != proto->corruptions();
178 RunInitStatusCallbackOnCallingSequence(
179 std::move(callback), std::move(callback_task_runner),
180 is_corrupt ? Enums::InitStatus::kCorrupt : Enums::InitStatus::kOK,
181 proto->migration_status(),
182 is_corrupt ? ProtoDatabaseSelector::ProtoDatabaseInitState::
183 kSharedDbClientCorrupt
184 : ProtoDatabaseSelector::ProtoDatabaseInitState::
185 kSharedDbClientSuccess);
186 }
187
CheckCorruptionAndRunInitCallback(const std::string & client_db_id,SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner,Enums::InitStatus status)188 void SharedProtoDatabase::CheckCorruptionAndRunInitCallback(
189 const std::string& client_db_id,
190 SharedClientInitCallback callback,
191 scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
192 Enums::InitStatus status) {
193 if (init_status_ == Enums::InitStatus::kOK) {
194 GetClientMetadataAsync(client_db_id, std::move(callback),
195 std::move(callback_task_runner));
196 return;
197 }
198 RunInitStatusCallbackOnCallingSequence(
199 std::move(callback), std::move(callback_task_runner), init_status_,
200 SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
201 ProtoDatabaseSelector::ProtoDatabaseInitState::kSharedLevelDbInitFailure);
202 }
203
204 // Setting |create_if_missing| to false allows us to test whether or not the
205 // shared database already exists, useful for migrating data from the shared
206 // database to a unique database if it exists.
207 // All clients planning to use the shared database should be setting
208 // |create_if_missing| to true. Setting this to false can result in unexpected
209 // behaviour since the ordering of Init calls may matter if some calls are made
210 // with this set to true, and others false.
Init(bool create_if_missing,const std::string & client_db_id,SharedClientInitCallback callback,scoped_refptr<base::SequencedTaskRunner> callback_task_runner)211 void SharedProtoDatabase::Init(
212 bool create_if_missing,
213 const std::string& client_db_id,
214 SharedClientInitCallback callback,
215 scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
216 // Try to create the db if any initialization request asked to do so.
217 create_if_missing_ = create_if_missing || create_if_missing_;
218
219 switch (init_state_) {
220 case InitState::kNotAttempted:
221 outstanding_init_requests_.emplace(std::make_unique<InitRequest>(
222 std::move(callback), std::move(callback_task_runner), client_db_id));
223
224 init_state_ = InitState::kInProgress;
225 // First, try to initialize the metadata database.
226 InitMetadataDatabase(0 /* attempt */, false /* corruption */);
227 break;
228
229 case InitState::kInProgress:
230 outstanding_init_requests_.emplace(std::make_unique<InitRequest>(
231 std::move(callback), std::move(callback_task_runner), client_db_id));
232 break;
233
234 // If we succeeded previously, just check for corruption status and run
235 // init callback.
236 case InitState::kSuccess:
237 CheckCorruptionAndRunInitCallback(client_db_id, std::move(callback),
238 std::move(callback_task_runner),
239 Enums::InitStatus::kOK);
240 break;
241
242 // If we previously failed then we run the callback with kError.
243 case InitState::kFailure:
244 RunInitStatusCallbackOnCallingSequence(
245 std::move(callback), std::move(callback_task_runner),
246 Enums::InitStatus::kError,
247 SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
248 ProtoDatabaseSelector::ProtoDatabaseInitState::
249 kSharedLevelDbInitFailure);
250 break;
251
252 case InitState::kNotFound:
253 if (create_if_missing_) {
254 // If the shared DB doesn't exist and we should create it if missing,
255 // then we skip initializing the metadata DB and initialize the shared
256 // DB directly.
257 DCHECK(metadata_);
258 init_state_ = InitState::kInProgress;
259 outstanding_init_requests_.emplace(std::make_unique<InitRequest>(
260 std::move(callback), std::move(callback_task_runner),
261 client_db_id));
262 InitDatabase();
263 } else {
264 // If the shared DB doesn't exist and we shouldn't create it if missing,
265 // then we run the callback with kInvalidOperation (which is not found).
266 RunInitStatusCallbackOnCallingSequence(
267 std::move(callback), std::move(callback_task_runner),
268 Enums::InitStatus::kInvalidOperation,
269 SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
270 ProtoDatabaseSelector::ProtoDatabaseInitState::
271 kSharedDbClientMissing);
272 }
273 break;
274 }
275 }
276
ProcessInitRequests(Enums::InitStatus status)277 void SharedProtoDatabase::ProcessInitRequests(Enums::InitStatus status) {
278 DCHECK(!outstanding_init_requests_.empty());
279
280 // The pairs are stored as (callback, callback_task_runner).
281 while (!outstanding_init_requests_.empty()) {
282 auto request = std::move(outstanding_init_requests_.front());
283 CheckCorruptionAndRunInitCallback(request->client_db_id,
284 std::move(request->callback),
285 std::move(request->task_runner), status);
286 outstanding_init_requests_.pop();
287 }
288 }
289
290 // We allow some number of attempts to be made to initialize the metadata
291 // database because it's crucial for the operation of the shared database. In
292 // the event that the metadata DB is corrupt, at least one retry will be made
293 // so that we create the DB from scratch again.
294 // |corruption| lets us know whether the retries are because of corruption.
InitMetadataDatabase(int attempt,bool corruption)295 void SharedProtoDatabase::InitMetadataDatabase(int attempt, bool corruption) {
296 DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
297
298 if (attempt >= kMaxInitMetaDatabaseAttempts) {
299 // TODO(crbug/1003951): |attempt| is always 0, need to save it and do the
300 // retry, or delete it.
301 init_state_ = InitState::kFailure;
302 init_status_ = Enums::InitStatus::kError;
303 ProcessInitRequests(init_status_);
304 return;
305 }
306
307 // TODO: figure out destroy on corruption param
308 metadata_db_wrapper_->Init(
309 base::BindOnce(&SharedProtoDatabase::OnMetadataInitComplete, this,
310 attempt, corruption));
311 }
312
OnMetadataInitComplete(int attempt,bool corruption,leveldb_proto::Enums::InitStatus status)313 void SharedProtoDatabase::OnMetadataInitComplete(
314 int attempt,
315 bool corruption,
316 leveldb_proto::Enums::InitStatus status) {
317 DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
318
319 bool success = status == Enums::kOK;
320
321 if (!success) {
322 init_state_ = InitState::kFailure;
323 init_status_ = Enums::InitStatus::kError;
324 ProcessInitRequests(init_status_);
325 return;
326 }
327
328 // Read or initialize the corruption count for this DB. If |corruption| is
329 // true, we initialize the counter to 1 right away so that all DBs are forced
330 // to treat the shared database as corrupt, we can't know for sure anymore.
331 metadata_db_wrapper_->GetEntry(
332 std::string(kGlobalMetadataKey),
333 base::BindOnce(&SharedProtoDatabase::OnGetGlobalMetadata, this,
334 corruption));
335 }
336
OnGetGlobalMetadata(bool corruption,bool success,std::unique_ptr<SharedDBMetadataProto> proto)337 void SharedProtoDatabase::OnGetGlobalMetadata(
338 bool corruption,
339 bool success,
340 std::unique_ptr<SharedDBMetadataProto> proto) {
341 DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
342 if (success && proto) {
343 // It existed so let's update our internal |corruption_count_|
344 metadata_ = std::move(proto);
345 InitDatabase();
346 return;
347 }
348
349 // We failed to get the global metadata, so we need to create it for the first
350 // time.
351 metadata_.reset(new SharedDBMetadataProto());
352 metadata_->set_corruptions(corruption ? 1U : 0U);
353 metadata_->clear_migration_status();
354 CommitUpdatedGlobalMetadata(
355 base::BindOnce(&SharedProtoDatabase::OnFinishCorruptionCountWrite, this));
356 }
357
OnFinishCorruptionCountWrite(bool success)358 void SharedProtoDatabase::OnFinishCorruptionCountWrite(
359 bool success) {
360 DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
361 // TODO(thildebr): Should we retry a few times if we fail this? It feels like
362 // if we fail to write this single value something serious happened with the
363 // metadata database.
364 if (!success) {
365 init_state_ = InitState::kFailure;
366 init_status_ = Enums::InitStatus::kError;
367 ProcessInitRequests(init_status_);
368 return;
369 }
370
371 InitDatabase();
372 }
373
InitDatabase()374 void SharedProtoDatabase::InitDatabase() {
375 DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
376 auto options = CreateSimpleOptions();
377 options.create_if_missing = create_if_missing_;
378 db_wrapper_->SetMetricsId(kSharedProtoDatabaseUmaName);
379 // |db_wrapper_| uses the same SequencedTaskRunner that Init is called on,
380 // so OnDatabaseInit will be called on the same sequence after Init.
381 // This means any callers to Init using the same TaskRunner can guarantee that
382 // the InitState will be final after Init is called.
383 db_wrapper_->InitWithDatabase(
384 db_.get(), db_dir_, options, false /* destroy_on_corruption */,
385 base::BindOnce(&SharedProtoDatabase::OnDatabaseInit, this,
386 create_if_missing_));
387 }
388
OnDatabaseInit(bool create_if_missing,Enums::InitStatus status)389 void SharedProtoDatabase::OnDatabaseInit(bool create_if_missing,
390 Enums::InitStatus status) {
391 DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
392
393 // Update the corruption counter locally and in the database.
394 if (status == Enums::InitStatus::kCorrupt) {
395 // TODO(thildebr): Find a clean way to break this into a function to be used
396 // by OnGetCorruptionCountEntry.
397 // If this fails, we actually send back a failure to the init callback.
398 // This may be overkill, but what happens if we don't update this value?
399 // Again, it seems like a failure to update here will indicate something
400 // serious has gone wrong with the metadata database.
401 metadata_->set_corruptions(metadata_->corruptions() + 1);
402
403 CommitUpdatedGlobalMetadata(base::BindOnce(
404 &SharedProtoDatabase::OnUpdateCorruptionCountAtInit, this));
405 return;
406 }
407
408 // If the previous initialization didn't create the database but a following
409 // request tries to create the db. Redo the initialization and create db.
410 if (create_if_missing_ && !create_if_missing &&
411 status == Enums::InitStatus::kInvalidOperation) {
412 DCHECK(init_state_ == InitState::kInProgress ||
413 init_state_ == InitState::kNotFound);
414 InitDatabase();
415 return;
416 }
417
418 init_status_ = status;
419
420 switch (status) {
421 case Enums::InitStatus::kOK:
422 init_state_ = InitState::kSuccess;
423 break;
424 case Enums::InitStatus::kInvalidOperation:
425 DCHECK(!create_if_missing_);
426 init_state_ = InitState::kNotFound;
427 break;
428 case Enums::InitStatus::kError:
429 case Enums::InitStatus::kNotInitialized:
430 case Enums::InitStatus::kCorrupt:
431 init_state_ = InitState::kFailure;
432 break;
433 }
434
435 ProcessInitRequests(status);
436
437 if (init_state_ == InitState::kSuccess) {
438 // Hold on to shared db until the remove operation is done or Shutdown()
439 // clears the task.
440 Callbacks::UpdateCallback keep_shared_db_alive =
441 base::BindOnce([](scoped_refptr<SharedProtoDatabase>, bool) {},
442 base::WrapRefCounted<>(this));
443 delete_obsolete_task_.Reset(base::BindOnce(
444 &SharedProtoDatabase::DestroyObsoleteSharedProtoDatabaseClients, this,
445 std::move(keep_shared_db_alive)));
446 task_runner_->PostDelayedTask(FROM_HERE, delete_obsolete_task_.callback(),
447 delete_obsolete_delay_);
448 }
449 }
450
Shutdown()451 void SharedProtoDatabase::Shutdown() {
452 if (!task_runner_->RunsTasksInCurrentSequence()) {
453 task_runner_->PostTask(
454 FROM_HERE, base::BindOnce(&SharedProtoDatabase::Shutdown, this));
455 return;
456 }
457 delete_obsolete_task_.Cancel();
458 }
459
OnUpdateCorruptionCountAtInit(bool success)460 void SharedProtoDatabase::OnUpdateCorruptionCountAtInit(bool success) {
461 // Return the success value of our write to update the corruption counter.
462 // This means that we return kError when the update fails, as a safeguard
463 // against clients trying to further persist data when something's gone
464 // wrong loading a single metadata proto.
465 init_state_ = success ? InitState::kSuccess : InitState::kFailure;
466 init_status_ =
467 success ? Enums::InitStatus::kCorrupt : Enums::InitStatus::kError;
468 ProcessInitRequests(init_status_);
469 }
470
CommitUpdatedGlobalMetadata(Callbacks::UpdateCallback callback)471 void SharedProtoDatabase::CommitUpdatedGlobalMetadata(
472 Callbacks::UpdateCallback callback) {
473 auto update_entries = std::make_unique<
474 std::vector<std::pair<std::string, SharedDBMetadataProto>>>();
475
476 SharedDBMetadataProto write_proto;
477 write_proto.CheckTypeAndMergeFrom(*metadata_);
478 update_entries->emplace_back(
479 std::make_pair(std::string(kGlobalMetadataKey), write_proto));
480 metadata_db_wrapper_->UpdateEntries(
481 std::move(update_entries), std::make_unique<std::vector<std::string>>(),
482 std::move(callback));
483 }
484
~SharedProtoDatabase()485 SharedProtoDatabase::~SharedProtoDatabase() {
486 task_runner_->DeleteSoon(FROM_HERE, std::move(db_));
487 task_runner_->DeleteSoon(FROM_HERE, std::move(metadata_db_wrapper_));
488 }
489
GetClientInitCallback(base::OnceCallback<void (std::unique_ptr<SharedProtoDatabaseClient>,Enums::InitStatus)> callback,std::unique_ptr<SharedProtoDatabaseClient> client,Enums::InitStatus status,SharedDBMetadataProto::MigrationStatus migration_status)490 void GetClientInitCallback(
491 base::OnceCallback<void(std::unique_ptr<SharedProtoDatabaseClient>,
492 Enums::InitStatus)> callback,
493 std::unique_ptr<SharedProtoDatabaseClient> client,
494 Enums::InitStatus status,
495 SharedDBMetadataProto::MigrationStatus migration_status) {
496 // |current_task_runner| is valid because Init already takes the current
497 // TaskRunner as a parameter and uses that to trigger this callback when it's
498 // finished.
499 DCHECK(base::SequencedTaskRunnerHandle::IsSet());
500 auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
501 if (status != Enums::InitStatus::kOK && status != Enums::InitStatus::kCorrupt)
502 client.reset();
503 // Set migration status of client. The metadata database was already updated.
504 if (client)
505 client->set_migration_status(migration_status);
506 current_task_runner->PostTask(
507 FROM_HERE,
508 base::BindOnce(std::move(callback), std::move(client), status));
509 }
510
GetClientAsync(ProtoDbType db_type,bool create_if_missing,base::OnceCallback<void (std::unique_ptr<SharedProtoDatabaseClient>,Enums::InitStatus)> callback)511 void SharedProtoDatabase::GetClientAsync(
512 ProtoDbType db_type,
513 bool create_if_missing,
514 base::OnceCallback<void(std::unique_ptr<SharedProtoDatabaseClient>,
515 Enums::InitStatus)> callback) {
516 auto client = GetClientInternal(db_type);
517 DCHECK(base::SequencedTaskRunnerHandle::IsSet());
518 auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
519 SharedProtoDatabaseClient* client_ptr = client.get();
520 task_runner_->PostTask(
521 FROM_HERE,
522 base::BindOnce(&SharedProtoDatabase::Init, this, create_if_missing,
523 client_ptr->client_db_id(),
524 base::BindOnce(&GetClientInitCallback, std::move(callback),
525 std::move(client)),
526 std::move(current_task_runner)));
527 }
528
529 // TODO(thildebr): Need to pass the client name into this call as well, and use
530 // it with the pending requests too so we can clean up the database.
531 std::unique_ptr<SharedProtoDatabaseClient>
GetClientForTesting(ProtoDbType db_type,bool create_if_missing,SharedClientInitCallback callback)532 SharedProtoDatabase::GetClientForTesting(ProtoDbType db_type,
533 bool create_if_missing,
534 SharedClientInitCallback callback) {
535 DCHECK(base::SequencedTaskRunnerHandle::IsSet());
536 auto current_task_runner = base::SequencedTaskRunnerHandle::Get();
537 auto client = GetClientInternal(db_type);
538 task_runner_->PostTask(
539 FROM_HERE,
540 base::BindOnce(&SharedProtoDatabase::Init, this, create_if_missing,
541 client->client_db_id(), std::move(callback),
542 std::move(current_task_runner)));
543 return client;
544 }
545
546 std::unique_ptr<SharedProtoDatabaseClient>
GetClientInternal(ProtoDbType db_type)547 SharedProtoDatabase::GetClientInternal(ProtoDbType db_type) {
548 return base::WrapUnique(new SharedProtoDatabaseClient(
549 std::make_unique<ProtoLevelDBWrapper>(task_runner_, db_.get()), db_type,
550 this));
551 }
552
DestroyObsoleteSharedProtoDatabaseClients(Callbacks::UpdateCallback done)553 void SharedProtoDatabase::DestroyObsoleteSharedProtoDatabaseClients(
554 Callbacks::UpdateCallback done) {
555 DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
556 // Create a ProtoLevelDBWrapper just like we create for each client, for
557 // deleting data from obsolete clients. It is fine to use the same wrapper to
558 // clear data from all clients. This object will be destroyed after clearing
559 // data for all these clients.
560 auto db_wrapper =
561 std::make_unique<ProtoLevelDBWrapper>(task_runner_, db_.get());
562 SharedProtoDatabaseClient::DestroyObsoleteSharedProtoDatabaseClients(
563 std::move(db_wrapper), std::move(done));
564 }
565
GetLevelDBForTesting() const566 LevelDB* SharedProtoDatabase::GetLevelDBForTesting() const {
567 return db_.get();
568 }
569
570 } // namespace leveldb_proto
571