Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sync/internal_api/public/attachments/on_disk_attachment_store.h" | 5 #include "sync/internal_api/public/attachments/on_disk_attachment_store.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback.h" | 8 #include "base/callback.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/sequenced_task_runner.h" | 11 #include "base/sequenced_task_runner.h" |
| 12 #include "sync/internal_api/attachments/proto/attachment_store.pb.h" | 12 #include "sync/internal_api/attachments/proto/attachment_store.pb.h" |
| 13 #include "sync/protocol/attachments.pb.h" | 13 #include "sync/protocol/attachments.pb.h" |
| 14 #include "third_party/leveldatabase/src/include/leveldb/db.h" | 14 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
| 15 #include "third_party/leveldatabase/src/include/leveldb/options.h" | 15 #include "third_party/leveldatabase/src/include/leveldb/options.h" |
| 16 #include "third_party/leveldatabase/src/include/leveldb/slice.h" | 16 #include "third_party/leveldatabase/src/include/leveldb/slice.h" |
| 17 #include "third_party/leveldatabase/src/include/leveldb/status.h" | 17 #include "third_party/leveldatabase/src/include/leveldb/status.h" |
| 18 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | |
| 18 | 19 |
| 19 namespace syncer { | 20 namespace syncer { |
| 20 | 21 |
| 21 namespace { | 22 namespace { |
| 22 | 23 |
| 23 // Prefix for records containing attachment data. | 24 // Prefix for records containing attachment data. |
| 24 const char kDataPrefix[] = "data-"; | 25 const char kDataPrefix[] = "data-"; |
| 25 | 26 |
| 27 // Prefix for records containing attachment metadata. | |
| 28 const char kMetadataPrefix[] = "metadata-"; | |
| 29 | |
| 26 const char kDatabaseMetadataKey[] = "database-metadata"; | 30 const char kDatabaseMetadataKey[] = "database-metadata"; |
| 27 | 31 |
| 28 const int32 kCurrentSchemaVersion = 1; | 32 const int32 kCurrentSchemaVersion = 1; |
| 29 | 33 |
| 30 const base::FilePath::CharType kLeveldbDirectory[] = | 34 const base::FilePath::CharType kLeveldbDirectory[] = |
| 31 FILE_PATH_LITERAL("leveldb"); | 35 FILE_PATH_LITERAL("leveldb"); |
| 32 | 36 |
| 33 leveldb::WriteOptions MakeWriteOptions() { | 37 leveldb::WriteOptions MakeWriteOptions() { |
| 34 leveldb::WriteOptions write_options; | 38 leveldb::WriteOptions write_options; |
| 35 write_options.sync = true; | 39 write_options.sync = true; |
| 36 return write_options; | 40 return write_options; |
| 37 } | 41 } |
| 38 | 42 |
| 43 leveldb::ReadOptions MakeDataReadOptions() { | |
| 44 leveldb::ReadOptions read_options; | |
| 45 // Attachment content is typically large and only read once. Don't cache it on | |
| 46 // db level. Don't verify checksums either, AttachmentStore will verify | |
| 47 // checksums separately. | |
| 48 read_options.fill_cache = false; | |
| 49 read_options.verify_checksums = false; | |
|
maniscalco
2014/10/30 16:40:20
WDYT about enabling leveldb checksums? Here's my
pavely
2014/10/30 18:28:36
I agree, letting leveldb can provide small additio
| |
| 50 return read_options; | |
| 51 } | |
| 52 | |
| 53 leveldb::ReadOptions MakeMetadataReadOptions() { | |
| 54 leveldb::ReadOptions read_options; | |
| 55 read_options.fill_cache = true; | |
| 56 read_options.verify_checksums = true; | |
| 57 return read_options; | |
| 58 } | |
| 59 | |
| 39 leveldb::Status ReadStoreMetadata( | 60 leveldb::Status ReadStoreMetadata( |
| 40 leveldb::DB* db, | 61 leveldb::DB* db, |
| 41 attachment_store_pb::AttachmentStoreMetadata* metadata) { | 62 attachment_store_pb::AttachmentStoreMetadata* metadata) { |
| 42 std::string data_str; | 63 std::string data_str; |
| 43 leveldb::ReadOptions read_options; | |
| 44 read_options.fill_cache = false; | |
| 45 read_options.verify_checksums = true; | |
| 46 | 64 |
| 47 leveldb::Status status = | 65 leveldb::Status status = |
| 48 db->Get(read_options, kDatabaseMetadataKey, &data_str); | 66 db->Get(MakeMetadataReadOptions(), kDatabaseMetadataKey, &data_str); |
| 49 if (!status.ok()) | 67 if (!status.ok()) |
| 50 return status; | 68 return status; |
| 51 if (!metadata->ParseFromString(data_str)) | 69 if (!metadata->ParseFromString(data_str)) |
| 52 return leveldb::Status::Corruption("Metadata record corruption"); | 70 return leveldb::Status::Corruption("Metadata record corruption"); |
| 53 return leveldb::Status::OK(); | 71 return leveldb::Status::OK(); |
| 54 } | 72 } |
| 55 | 73 |
| 56 leveldb::Status WriteStoreMetadata( | 74 leveldb::Status WriteStoreMetadata( |
| 57 leveldb::DB* db, | 75 leveldb::DB* db, |
| 58 const attachment_store_pb::AttachmentStoreMetadata& metadata) { | 76 const attachment_store_pb::AttachmentStoreMetadata& metadata) { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 72 OnDiskAttachmentStore::~OnDiskAttachmentStore() { | 90 OnDiskAttachmentStore::~OnDiskAttachmentStore() { |
| 73 } | 91 } |
| 74 | 92 |
| 75 void OnDiskAttachmentStore::Read(const AttachmentIdList& ids, | 93 void OnDiskAttachmentStore::Read(const AttachmentIdList& ids, |
| 76 const ReadCallback& callback) { | 94 const ReadCallback& callback) { |
| 77 DCHECK(CalledOnValidThread()); | 95 DCHECK(CalledOnValidThread()); |
| 78 DCHECK(db_); | 96 DCHECK(db_); |
| 79 scoped_ptr<AttachmentMap> result_map(new AttachmentMap()); | 97 scoped_ptr<AttachmentMap> result_map(new AttachmentMap()); |
| 80 scoped_ptr<AttachmentIdList> unavailable_attachments(new AttachmentIdList()); | 98 scoped_ptr<AttachmentIdList> unavailable_attachments(new AttachmentIdList()); |
| 81 | 99 |
| 82 leveldb::ReadOptions read_options; | |
| 83 // Attachment content is typically large and only read once. Don't cache it on | |
| 84 // db level. | |
| 85 read_options.fill_cache = false; | |
| 86 read_options.verify_checksums = true; | |
| 87 | |
| 88 AttachmentIdList::const_iterator iter = ids.begin(); | 100 AttachmentIdList::const_iterator iter = ids.begin(); |
| 89 const AttachmentIdList::const_iterator end = ids.end(); | 101 const AttachmentIdList::const_iterator end = ids.end(); |
| 90 for (; iter != end; ++iter) { | 102 for (; iter != end; ++iter) { |
| 91 const std::string key = MakeDataKeyFromAttachmentId(*iter); | 103 scoped_ptr<Attachment> attachment = ReadSingleAttachment(*iter); |
| 92 std::string data_str; | 104 if (attachment) { |
| 93 leveldb::Status status = db_->Get(read_options, key, &data_str); | 105 result_map->insert(std::make_pair(*iter, *attachment)); |
| 94 if (!status.ok()) { | 106 } else { |
| 95 DVLOG(1) << "DB::Get failed: status=" << status.ToString(); | |
| 96 unavailable_attachments->push_back(*iter); | 107 unavailable_attachments->push_back(*iter); |
| 97 continue; | |
| 98 } | 108 } |
| 99 scoped_refptr<base::RefCountedMemory> data = | |
| 100 base::RefCountedString::TakeString(&data_str); | |
| 101 Attachment attachment = Attachment::CreateWithId(*iter, data); | |
| 102 result_map->insert(std::make_pair(*iter, attachment)); | |
| 103 } | 109 } |
| 104 | 110 |
| 105 Result result_code = | 111 Result result_code = |
| 106 unavailable_attachments->empty() ? SUCCESS : UNSPECIFIED_ERROR; | 112 unavailable_attachments->empty() ? SUCCESS : UNSPECIFIED_ERROR; |
| 107 callback_task_runner_->PostTask( | 113 callback_task_runner_->PostTask( |
| 108 FROM_HERE, | 114 FROM_HERE, |
| 109 base::Bind(callback, | 115 base::Bind(callback, |
| 110 result_code, | 116 result_code, |
| 111 base::Passed(&result_map), | 117 base::Passed(&result_map), |
| 112 base::Passed(&unavailable_attachments))); | 118 base::Passed(&unavailable_attachments))); |
| 113 } | 119 } |
| 114 | 120 |
| 115 void OnDiskAttachmentStore::Write(const AttachmentList& attachments, | 121 void OnDiskAttachmentStore::Write(const AttachmentList& attachments, |
| 116 const WriteCallback& callback) { | 122 const WriteCallback& callback) { |
| 117 DCHECK(CalledOnValidThread()); | 123 DCHECK(CalledOnValidThread()); |
| 118 DCHECK(db_); | 124 DCHECK(db_); |
| 119 Result result_code = SUCCESS; | 125 Result result_code = SUCCESS; |
| 120 | 126 |
| 121 leveldb::ReadOptions read_options; | |
| 122 read_options.fill_cache = false; | |
| 123 read_options.verify_checksums = true; | |
| 124 | |
| 125 leveldb::WriteOptions write_options = MakeWriteOptions(); | |
| 126 | |
| 127 AttachmentList::const_iterator iter = attachments.begin(); | 127 AttachmentList::const_iterator iter = attachments.begin(); |
| 128 const AttachmentList::const_iterator end = attachments.end(); | 128 const AttachmentList::const_iterator end = attachments.end(); |
| 129 for (; iter != end; ++iter) { | 129 for (; iter != end; ++iter) { |
| 130 const std::string key = MakeDataKeyFromAttachmentId(iter->GetId()); | 130 if (!WriteSingleAttachment(*iter)) |
| 131 | |
| 132 std::string data_str; | |
| 133 // TODO(pavely): crbug/424304 This read is expensive. When I add metadata | |
| 134 // records this read will target metadata record instead of payload record. | |
| 135 leveldb::Status status = db_->Get(read_options, key, &data_str); | |
| 136 if (status.ok()) { | |
| 137 // Entry exists, don't overwrite. | |
| 138 continue; | |
| 139 } else if (!status.IsNotFound()) { | |
| 140 // Entry exists but failed to read. | |
| 141 DVLOG(1) << "DB::Get failed: status=" << status.ToString(); | |
| 142 result_code = UNSPECIFIED_ERROR; | 131 result_code = UNSPECIFIED_ERROR; |
| 143 continue; | |
| 144 } | |
| 145 DCHECK(status.IsNotFound()); | |
| 146 | |
| 147 scoped_refptr<base::RefCountedMemory> data = iter->GetData(); | |
| 148 leveldb::Slice data_slice(data->front_as<char>(), data->size()); | |
| 149 status = db_->Put(write_options, key, data_slice); | |
| 150 if (!status.ok()) { | |
| 151 // Failed to write. | |
| 152 DVLOG(1) << "DB::Put failed: status=" << status.ToString(); | |
| 153 result_code = UNSPECIFIED_ERROR; | |
| 154 } | |
| 155 } | 132 } |
| 156 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code)); | 133 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code)); |
| 157 } | 134 } |
| 158 | 135 |
| 159 void OnDiskAttachmentStore::Drop(const AttachmentIdList& ids, | 136 void OnDiskAttachmentStore::Drop(const AttachmentIdList& ids, |
| 160 const DropCallback& callback) { | 137 const DropCallback& callback) { |
| 161 DCHECK(CalledOnValidThread()); | 138 DCHECK(CalledOnValidThread()); |
| 162 DCHECK(db_); | 139 DCHECK(db_); |
| 163 Result result_code = SUCCESS; | 140 Result result_code = SUCCESS; |
| 164 leveldb::WriteOptions write_options = MakeWriteOptions(); | 141 leveldb::WriteOptions write_options = MakeWriteOptions(); |
| 165 AttachmentIdList::const_iterator iter = ids.begin(); | 142 AttachmentIdList::const_iterator iter = ids.begin(); |
| 166 const AttachmentIdList::const_iterator end = ids.end(); | 143 const AttachmentIdList::const_iterator end = ids.end(); |
| 167 for (; iter != end; ++iter) { | 144 for (; iter != end; ++iter) { |
| 168 const std::string key = MakeDataKeyFromAttachmentId(*iter); | 145 leveldb::WriteBatch write_batch; |
| 169 leveldb::Status status = db_->Delete(write_options, key); | 146 write_batch.Delete(MakeDataKeyFromAttachmentId(*iter)); |
| 147 write_batch.Delete(MakeMetadataKeyFromAttachmentId(*iter)); | |
| 148 | |
| 149 leveldb::Status status = db_->Write(write_options, &write_batch); | |
| 170 if (!status.ok()) { | 150 if (!status.ok()) { |
| 171 // DB::Delete doesn't check if record exists, it returns ok just like | 151 // DB::Delete doesn't check if record exists, it returns ok just like |
| 172 // AttachmentStore::Drop should. | 152 // AttachmentStore::Drop should. |
| 173 DVLOG(1) << "DB::Delete failed: status=" << status.ToString(); | 153 DVLOG(1) << "DB::Write failed: status=" << status.ToString(); |
| 174 result_code = UNSPECIFIED_ERROR; | 154 result_code = UNSPECIFIED_ERROR; |
| 175 } | 155 } |
| 176 } | 156 } |
| 177 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code)); | 157 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code)); |
| 178 } | 158 } |
| 179 | 159 |
| 180 AttachmentStore::Result OnDiskAttachmentStore::OpenOrCreate( | 160 AttachmentStore::Result OnDiskAttachmentStore::OpenOrCreate( |
| 181 const base::FilePath& path) { | 161 const base::FilePath& path) { |
| 182 DCHECK(CalledOnValidThread()); | 162 DCHECK(CalledOnValidThread()); |
| 183 DCHECK(!db_); | 163 DCHECK(!db_); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 220 | 200 |
| 221 if (metadata.schema_version() != kCurrentSchemaVersion) { | 201 if (metadata.schema_version() != kCurrentSchemaVersion) { |
| 222 DVLOG(1) << "Unknown schema version: " << metadata.schema_version(); | 202 DVLOG(1) << "Unknown schema version: " << metadata.schema_version(); |
| 223 return UNSPECIFIED_ERROR; | 203 return UNSPECIFIED_ERROR; |
| 224 } | 204 } |
| 225 | 205 |
| 226 db_ = db.Pass(); | 206 db_ = db.Pass(); |
| 227 return SUCCESS; | 207 return SUCCESS; |
| 228 } | 208 } |
| 229 | 209 |
| 210 scoped_ptr<Attachment> OnDiskAttachmentStore::ReadSingleAttachment( | |
| 211 const AttachmentId& attachment_id) { | |
| 212 scoped_ptr<Attachment> attachment; | |
| 213 | |
| 214 const std::string key = MakeDataKeyFromAttachmentId(attachment_id); | |
| 215 std::string data_str; | |
| 216 leveldb::Status status = db_->Get(MakeDataReadOptions(), key, &data_str); | |
| 217 if (status.ok()) { | |
| 218 scoped_refptr<base::RefCountedMemory> data = | |
| 219 base::RefCountedString::TakeString(&data_str); | |
| 220 attachment.reset( | |
| 221 new Attachment(Attachment::CreateWithId(attachment_id, data))); | |
| 222 } else { | |
| 223 DVLOG(1) << "DB::Get failed: status=" << status.ToString(); | |
| 224 } | |
| 225 return attachment.Pass(); | |
| 226 } | |
| 227 | |
| 228 bool OnDiskAttachmentStore::WriteSingleAttachment( | |
| 229 const Attachment& attachment) { | |
| 230 const std::string metadata_key = | |
| 231 MakeMetadataKeyFromAttachmentId(attachment.GetId()); | |
| 232 const std::string data_key = MakeDataKeyFromAttachmentId(attachment.GetId()); | |
| 233 | |
| 234 std::string metadata_str; | |
| 235 leveldb::Status status = | |
| 236 db_->Get(MakeMetadataReadOptions(), metadata_key, &metadata_str); | |
| 237 if (status.ok()) { | |
| 238 // Entry exists, don't overwrite. | |
| 239 return true; | |
| 240 } else if (!status.IsNotFound()) { | |
| 241 // Entry exists but failed to read. | |
| 242 DVLOG(1) << "DB::Get failed: status=" << status.ToString(); | |
| 243 return false; | |
| 244 } | |
| 245 DCHECK(status.IsNotFound()); | |
| 246 | |
| 247 leveldb::WriteBatch write_batch; | |
| 248 // Write metadata. | |
| 249 attachment_store_pb::AttachmentRecordMetadata metadata; | |
| 250 metadata.set_attachment_size(attachment.GetData()->size()); | |
| 251 metadata_str = metadata.SerializeAsString(); | |
| 252 write_batch.Put(metadata_key, metadata_str); | |
| 253 // Write data. | |
| 254 scoped_refptr<base::RefCountedMemory> data = attachment.GetData(); | |
| 255 leveldb::Slice data_slice(data->front_as<char>(), data->size()); | |
| 256 write_batch.Put(data_key, data_slice); | |
| 257 | |
| 258 status = db_->Write(MakeWriteOptions(), &write_batch); | |
| 259 if (!status.ok()) { | |
| 260 // Failed to write. | |
| 261 DVLOG(1) << "DB::Write failed: status=" << status.ToString(); | |
| 262 return false; | |
| 263 } | |
| 264 return true; | |
| 265 } | |
| 266 | |
| 230 std::string OnDiskAttachmentStore::MakeDataKeyFromAttachmentId( | 267 std::string OnDiskAttachmentStore::MakeDataKeyFromAttachmentId( |
| 231 const AttachmentId& attachment_id) { | 268 const AttachmentId& attachment_id) { |
| 232 std::string key = kDataPrefix + attachment_id.GetProto().unique_id(); | 269 std::string key = kDataPrefix + attachment_id.GetProto().unique_id(); |
| 233 return key; | 270 return key; |
| 234 } | 271 } |
| 235 | 272 |
| 273 std::string OnDiskAttachmentStore::MakeMetadataKeyFromAttachmentId( | |
| 274 const AttachmentId& attachment_id) { | |
| 275 std::string key = kMetadataPrefix + attachment_id.GetProto().unique_id(); | |
| 276 return key; | |
| 277 } | |
| 278 | |
| 236 } // namespace syncer | 279 } // namespace syncer |
| OLD | NEW |