Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(263)

Side by Side Diff: sync/internal_api/attachments/on_disk_attachment_store.cc

Issue 690723004: Add per attachment metadata records. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698