model_type_store_backend.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // Copyright 2015 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. #include "components/sync/model/model_type_store_backend.h"
  5. #include <utility>
  6. #include "base/memory/ptr_util.h"
  7. #include "base/metrics/histogram_functions.h"
  8. #include "base/strings/strcat.h"
  9. #include "base/threading/sequenced_task_runner_handle.h"
  10. #include "components/sync/protocol/model_type_store_schema_descriptor.pb.h"
  11. #include "third_party/leveldatabase/env_chromium.h"
  12. #include "third_party/leveldatabase/leveldb_chrome.h"
  13. #include "third_party/leveldatabase/src/include/leveldb/db.h"
  14. #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
  15. #include "third_party/leveldatabase/src/include/leveldb/options.h"
  16. #include "third_party/leveldatabase/src/include/leveldb/slice.h"
  17. #include "third_party/leveldatabase/src/include/leveldb/status.h"
  18. #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
  19. using sync_pb::ModelTypeStoreSchemaDescriptor;
  20. namespace syncer {
  21. const int64_t kInvalidSchemaVersion = -1;
  22. const int64_t ModelTypeStoreBackend::kLatestSchemaVersion = 1;
  23. const char ModelTypeStoreBackend::kDBSchemaDescriptorRecordId[] =
  24. "_mts_schema_descriptor";
  25. namespace {
  26. void LogDbStatusByCallingSiteIfNeeded(const std::string& calling_site,
  27. leveldb::Status status) {
  28. if (status.ok()) {
  29. return;
  30. }
  31. const std::string histogram_name =
  32. "Sync.ModelTypeStoreBackendError." + calling_site;
  33. base::UmaHistogramEnumeration(histogram_name,
  34. leveldb_env::GetLevelDBStatusUMAValue(status),
  35. leveldb_env::LEVELDB_STATUS_MAX);
  36. }
  37. } // namespace
  38. ModelTypeStoreBackend::CustomOnTaskRunnerDeleter::CustomOnTaskRunnerDeleter(
  39. scoped_refptr<base::SequencedTaskRunner> task_runner)
  40. : task_runner_(std::move(task_runner)) {}
  41. ModelTypeStoreBackend::CustomOnTaskRunnerDeleter::CustomOnTaskRunnerDeleter(
  42. CustomOnTaskRunnerDeleter&&) = default;
  43. ModelTypeStoreBackend::CustomOnTaskRunnerDeleter&
  44. ModelTypeStoreBackend::CustomOnTaskRunnerDeleter::operator=(
  45. CustomOnTaskRunnerDeleter&&) = default;
  46. ModelTypeStoreBackend::CustomOnTaskRunnerDeleter::~CustomOnTaskRunnerDeleter() =
  47. default;
  48. // static
  49. scoped_refptr<ModelTypeStoreBackend>
  50. ModelTypeStoreBackend::CreateInMemoryForTest() {
  51. std::unique_ptr<leveldb::Env> env =
  52. leveldb_chrome::NewMemEnv("ModelTypeStore");
  53. std::string test_directory_str;
  54. env->GetTestDirectory(&test_directory_str);
  55. const base::FilePath path = base::FilePath::FromUTF8Unsafe(test_directory_str)
  56. .Append(FILE_PATH_LITERAL("in-memory"));
  57. scoped_refptr<ModelTypeStoreBackend> backend =
  58. new ModelTypeStoreBackend(std::move(env));
  59. absl::optional<ModelError> error = backend->Init(path);
  60. DCHECK(!error);
  61. return backend;
  62. }
  63. // static
  64. scoped_refptr<ModelTypeStoreBackend>
  65. ModelTypeStoreBackend::CreateUninitialized() {
  66. return new ModelTypeStoreBackend(/*env=*/nullptr);
  67. }
  68. // This is a refcounted class and the destructor is safe on any sequence and
  69. // hence DCHECK_CALLED_ON_VALID_SEQUENCE is omitted. Note that blocking
  70. // operations in leveldb's DBImpl::~DBImpl are posted to the backend sequence
  71. // due to the custom deleter used for |db_|.
  72. ModelTypeStoreBackend::~ModelTypeStoreBackend() = default;
  73. absl::optional<ModelError> ModelTypeStoreBackend::Init(
  74. const base::FilePath& path) {
  75. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  76. DCHECK(!IsInitialized());
  77. const std::string path_str = path.AsUTF8Unsafe();
  78. leveldb::Status status = OpenDatabase(path_str, env_.get());
  79. if (status.IsCorruption()) {
  80. DCHECK(db_ == nullptr);
  81. status = DestroyDatabase(path_str, env_.get());
  82. if (status.ok())
  83. status = OpenDatabase(path_str, env_.get());
  84. }
  85. LogDbStatusByCallingSiteIfNeeded("Init", status);
  86. if (!status.ok()) {
  87. DCHECK(db_ == nullptr);
  88. return ModelError(FROM_HERE, status.ToString());
  89. }
  90. int64_t current_version = GetStoreVersion();
  91. if (current_version == kInvalidSchemaVersion) {
  92. return ModelError(FROM_HERE, "Invalid schema descriptor");
  93. }
  94. if (current_version != kLatestSchemaVersion) {
  95. absl::optional<ModelError> error =
  96. Migrate(current_version, kLatestSchemaVersion);
  97. if (error) {
  98. return error;
  99. }
  100. }
  101. return absl::nullopt;
  102. }
  103. bool ModelTypeStoreBackend::IsInitialized() const {
  104. return db_ != nullptr;
  105. }
  106. ModelTypeStoreBackend::ModelTypeStoreBackend(std::unique_ptr<leveldb::Env> env)
  107. : env_(std::move(env)), db_(nullptr, CustomOnTaskRunnerDeleter(nullptr)) {
  108. // It's OK to construct this class in a sequence and Init() it elsewhere.
  109. DETACH_FROM_SEQUENCE(sequence_checker_);
  110. }
  111. leveldb::Status ModelTypeStoreBackend::OpenDatabase(const std::string& path,
  112. leveldb::Env* env) {
  113. leveldb_env::Options options;
  114. options.create_if_missing = true;
  115. options.paranoid_checks = true;
  116. options.write_buffer_size = 512 * 1024;
  117. if (env)
  118. options.env = env;
  119. std::unique_ptr<leveldb::DB> tmp_db;
  120. const leveldb::Status status = leveldb_env::OpenDB(options, path, &tmp_db);
  121. // Make sure that the database is destroyed on the same sequence where it was
  122. // created.
  123. db_ = std::unique_ptr<leveldb::DB, CustomOnTaskRunnerDeleter>(
  124. tmp_db.release(),
  125. CustomOnTaskRunnerDeleter(base::SequencedTaskRunnerHandle::Get()));
  126. return status;
  127. }
  128. leveldb::Status ModelTypeStoreBackend::DestroyDatabase(const std::string& path,
  129. leveldb::Env* env) {
  130. leveldb_env::Options options;
  131. if (env)
  132. options.env = env;
  133. return leveldb::DestroyDB(path, options);
  134. }
  135. absl::optional<ModelError> ModelTypeStoreBackend::ReadRecordsWithPrefix(
  136. const std::string& prefix,
  137. const ModelTypeStore::IdList& id_list,
  138. ModelTypeStore::RecordList* record_list,
  139. ModelTypeStore::IdList* missing_id_list) {
  140. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  141. DCHECK(db_);
  142. record_list->reserve(id_list.size());
  143. leveldb::ReadOptions read_options;
  144. read_options.verify_checksums = true;
  145. read_options.fill_cache = false;
  146. std::string key;
  147. std::string value;
  148. for (const std::string& id : id_list) {
  149. key = prefix + id;
  150. leveldb::Status status = db_->Get(read_options, key, &value);
  151. LogDbStatusByCallingSiteIfNeeded("ReadRecords", status);
  152. if (status.ok()) {
  153. record_list->emplace_back(id, value);
  154. } else if (status.IsNotFound()) {
  155. missing_id_list->push_back(id);
  156. } else {
  157. return ModelError(FROM_HERE, status.ToString());
  158. }
  159. }
  160. return absl::nullopt;
  161. }
  162. absl::optional<ModelError> ModelTypeStoreBackend::ReadAllRecordsWithPrefix(
  163. const std::string& prefix,
  164. ModelTypeStore::RecordList* record_list) {
  165. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  166. DCHECK(db_);
  167. leveldb::ReadOptions read_options;
  168. read_options.verify_checksums = true;
  169. read_options.fill_cache = false;
  170. std::unique_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
  171. const leveldb::Slice prefix_slice(prefix);
  172. for (iter->Seek(prefix_slice); iter->Valid(); iter->Next()) {
  173. leveldb::Slice key = iter->key();
  174. if (!key.starts_with(prefix_slice))
  175. break;
  176. key.remove_prefix(prefix_slice.size());
  177. record_list->emplace_back(key.ToString(), iter->value().ToString());
  178. }
  179. LogDbStatusByCallingSiteIfNeeded("ReadAllRecords", iter->status());
  180. return iter->status().ok() ? absl::nullopt
  181. : absl::optional<ModelError>(
  182. {FROM_HERE, iter->status().ToString()});
  183. }
  184. absl::optional<ModelError> ModelTypeStoreBackend::WriteModifications(
  185. std::unique_ptr<leveldb::WriteBatch> write_batch) {
  186. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  187. DCHECK(db_);
  188. leveldb::Status status =
  189. db_->Write(leveldb::WriteOptions(), write_batch.get());
  190. LogDbStatusByCallingSiteIfNeeded("WriteModifications", status);
  191. return status.ok()
  192. ? absl::nullopt
  193. : absl::optional<ModelError>({FROM_HERE, status.ToString()});
  194. }
  195. absl::optional<ModelError>
  196. ModelTypeStoreBackend::DeleteDataAndMetadataForPrefix(
  197. const std::string& prefix) {
  198. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  199. DCHECK(db_);
  200. leveldb::WriteBatch write_batch;
  201. leveldb::ReadOptions read_options;
  202. read_options.fill_cache = false;
  203. std::unique_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
  204. const leveldb::Slice prefix_slice(prefix);
  205. for (iter->Seek(prefix_slice); iter->Valid(); iter->Next()) {
  206. leveldb::Slice key = iter->key();
  207. if (!key.starts_with(prefix_slice))
  208. break;
  209. write_batch.Delete(key);
  210. }
  211. leveldb::Status status = db_->Write(leveldb::WriteOptions(), &write_batch);
  212. LogDbStatusByCallingSiteIfNeeded("DeleteData", status);
  213. return status.ok()
  214. ? absl::nullopt
  215. : absl::optional<ModelError>({FROM_HERE, status.ToString()});
  216. }
  217. absl::optional<ModelError> ModelTypeStoreBackend::MigrateForTest(
  218. int64_t current_version,
  219. int64_t desired_version) {
  220. return Migrate(current_version, desired_version);
  221. }
  222. int64_t ModelTypeStoreBackend::GetStoreVersionForTest() {
  223. return GetStoreVersion();
  224. }
  225. int64_t ModelTypeStoreBackend::GetStoreVersion() {
  226. DCHECK(db_);
  227. leveldb::ReadOptions read_options;
  228. read_options.verify_checksums = true;
  229. read_options.fill_cache = false;
  230. std::string value;
  231. ModelTypeStoreSchemaDescriptor schema_descriptor;
  232. leveldb::Status status =
  233. db_->Get(read_options, kDBSchemaDescriptorRecordId, &value);
  234. if (status.IsNotFound()) {
  235. return 0;
  236. } else if (!status.ok() || !schema_descriptor.ParseFromString(value)) {
  237. LogDbStatusByCallingSiteIfNeeded("GetStoreVersion", status);
  238. return kInvalidSchemaVersion;
  239. }
  240. return schema_descriptor.version_number();
  241. }
  242. absl::optional<ModelError> ModelTypeStoreBackend::Migrate(
  243. int64_t current_version,
  244. int64_t desired_version) {
  245. DCHECK(db_);
  246. if (current_version == 0) {
  247. if (Migrate0To1()) {
  248. current_version = 1;
  249. }
  250. }
  251. if (current_version == desired_version) {
  252. return absl::nullopt;
  253. } else if (current_version > desired_version) {
  254. return ModelError(FROM_HERE, "Schema version too high");
  255. } else {
  256. return ModelError(FROM_HERE, "Schema upgrade failed");
  257. }
  258. }
  259. bool ModelTypeStoreBackend::Migrate0To1() {
  260. DCHECK(db_);
  261. ModelTypeStoreSchemaDescriptor schema_descriptor;
  262. schema_descriptor.set_version_number(1);
  263. leveldb::Status status =
  264. db_->Put(leveldb::WriteOptions(), kDBSchemaDescriptorRecordId,
  265. schema_descriptor.SerializeAsString());
  266. return status.ok();
  267. }
  268. } // namespace syncer