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

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: Changes after feedback. 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-";
stanisc 2014/10/30 18:56:59 It probably doesn't matter in this case but I'd me
pavely 2014/10/30 19:54:15 One scenario that we will likely need to do is sca
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.
47 read_options.fill_cache = false;
48 read_options.verify_checksums = true;
49 return read_options;
50 }
51
52 leveldb::ReadOptions MakeMetadataReadOptions() {
53 leveldb::ReadOptions read_options;
54 read_options.fill_cache = true;
55 read_options.verify_checksums = true;
56 return read_options;
57 }
58
39 leveldb::Status ReadStoreMetadata( 59 leveldb::Status ReadStoreMetadata(
40 leveldb::DB* db, 60 leveldb::DB* db,
41 attachment_store_pb::AttachmentStoreMetadata* metadata) { 61 attachment_store_pb::StoreMetadata* metadata) {
42 std::string data_str; 62 std::string data_str;
43 leveldb::ReadOptions read_options;
44 read_options.fill_cache = false;
45 read_options.verify_checksums = true;
46 63
47 leveldb::Status status = 64 leveldb::Status status =
48 db->Get(read_options, kDatabaseMetadataKey, &data_str); 65 db->Get(MakeMetadataReadOptions(), kDatabaseMetadataKey, &data_str);
49 if (!status.ok()) 66 if (!status.ok())
50 return status; 67 return status;
51 if (!metadata->ParseFromString(data_str)) 68 if (!metadata->ParseFromString(data_str))
52 return leveldb::Status::Corruption("Metadata record corruption"); 69 return leveldb::Status::Corruption("Metadata record corruption");
53 return leveldb::Status::OK(); 70 return leveldb::Status::OK();
54 } 71 }
55 72
56 leveldb::Status WriteStoreMetadata( 73 leveldb::Status WriteStoreMetadata(
57 leveldb::DB* db, 74 leveldb::DB* db,
58 const attachment_store_pb::AttachmentStoreMetadata& metadata) { 75 const attachment_store_pb::StoreMetadata& metadata) {
59 std::string data_str; 76 std::string data_str;
60 77
61 metadata.SerializeToString(&data_str); 78 metadata.SerializeToString(&data_str);
62 return db->Put(MakeWriteOptions(), kDatabaseMetadataKey, data_str); 79 return db->Put(MakeWriteOptions(), kDatabaseMetadataKey, data_str);
63 } 80 }
64 81
65 } // namespace 82 } // namespace
66 83
67 OnDiskAttachmentStore::OnDiskAttachmentStore( 84 OnDiskAttachmentStore::OnDiskAttachmentStore(
68 const scoped_refptr<base::SequencedTaskRunner>& callback_task_runner) 85 const scoped_refptr<base::SequencedTaskRunner>& callback_task_runner)
69 : callback_task_runner_(callback_task_runner) { 86 : callback_task_runner_(callback_task_runner) {
70 } 87 }
71 88
72 OnDiskAttachmentStore::~OnDiskAttachmentStore() { 89 OnDiskAttachmentStore::~OnDiskAttachmentStore() {
73 } 90 }
74 91
75 void OnDiskAttachmentStore::Read(const AttachmentIdList& ids, 92 void OnDiskAttachmentStore::Read(const AttachmentIdList& ids,
76 const ReadCallback& callback) { 93 const ReadCallback& callback) {
77 DCHECK(CalledOnValidThread()); 94 DCHECK(CalledOnValidThread());
78 DCHECK(db_); 95 DCHECK(db_);
79 scoped_ptr<AttachmentMap> result_map(new AttachmentMap()); 96 scoped_ptr<AttachmentMap> result_map(new AttachmentMap());
80 scoped_ptr<AttachmentIdList> unavailable_attachments(new AttachmentIdList()); 97 scoped_ptr<AttachmentIdList> unavailable_attachments(new AttachmentIdList());
81 98
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(); 99 AttachmentIdList::const_iterator iter = ids.begin();
89 const AttachmentIdList::const_iterator end = ids.end(); 100 const AttachmentIdList::const_iterator end = ids.end();
90 for (; iter != end; ++iter) { 101 for (; iter != end; ++iter) {
91 const std::string key = MakeDataKeyFromAttachmentId(*iter); 102 scoped_ptr<Attachment> attachment = ReadSingleAttachment(*iter);
92 std::string data_str; 103 if (attachment) {
93 leveldb::Status status = db_->Get(read_options, key, &data_str); 104 result_map->insert(std::make_pair(*iter, *attachment));
94 if (!status.ok()) { 105 } else {
95 DVLOG(1) << "DB::Get failed: status=" << status.ToString();
96 unavailable_attachments->push_back(*iter); 106 unavailable_attachments->push_back(*iter);
97 continue;
98 } 107 }
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 } 108 }
104 109
105 Result result_code = 110 Result result_code =
106 unavailable_attachments->empty() ? SUCCESS : UNSPECIFIED_ERROR; 111 unavailable_attachments->empty() ? SUCCESS : UNSPECIFIED_ERROR;
107 callback_task_runner_->PostTask( 112 callback_task_runner_->PostTask(
108 FROM_HERE, 113 FROM_HERE,
109 base::Bind(callback, 114 base::Bind(callback,
110 result_code, 115 result_code,
111 base::Passed(&result_map), 116 base::Passed(&result_map),
112 base::Passed(&unavailable_attachments))); 117 base::Passed(&unavailable_attachments)));
113 } 118 }
114 119
115 void OnDiskAttachmentStore::Write(const AttachmentList& attachments, 120 void OnDiskAttachmentStore::Write(const AttachmentList& attachments,
116 const WriteCallback& callback) { 121 const WriteCallback& callback) {
117 DCHECK(CalledOnValidThread()); 122 DCHECK(CalledOnValidThread());
118 DCHECK(db_); 123 DCHECK(db_);
119 Result result_code = SUCCESS; 124 Result result_code = SUCCESS;
120 125
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(); 126 AttachmentList::const_iterator iter = attachments.begin();
128 const AttachmentList::const_iterator end = attachments.end(); 127 const AttachmentList::const_iterator end = attachments.end();
129 for (; iter != end; ++iter) { 128 for (; iter != end; ++iter) {
130 const std::string key = MakeDataKeyFromAttachmentId(iter->GetId()); 129 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; 130 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 } 131 }
156 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code)); 132 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code));
157 } 133 }
158 134
159 void OnDiskAttachmentStore::Drop(const AttachmentIdList& ids, 135 void OnDiskAttachmentStore::Drop(const AttachmentIdList& ids,
160 const DropCallback& callback) { 136 const DropCallback& callback) {
161 DCHECK(CalledOnValidThread()); 137 DCHECK(CalledOnValidThread());
162 DCHECK(db_); 138 DCHECK(db_);
163 Result result_code = SUCCESS; 139 Result result_code = SUCCESS;
164 leveldb::WriteOptions write_options = MakeWriteOptions(); 140 leveldb::WriteOptions write_options = MakeWriteOptions();
165 AttachmentIdList::const_iterator iter = ids.begin(); 141 AttachmentIdList::const_iterator iter = ids.begin();
166 const AttachmentIdList::const_iterator end = ids.end(); 142 const AttachmentIdList::const_iterator end = ids.end();
167 for (; iter != end; ++iter) { 143 for (; iter != end; ++iter) {
168 const std::string key = MakeDataKeyFromAttachmentId(*iter); 144 leveldb::WriteBatch write_batch;
169 leveldb::Status status = db_->Delete(write_options, key); 145 write_batch.Delete(MakeDataKeyFromAttachmentId(*iter));
146 write_batch.Delete(MakeMetadataKeyFromAttachmentId(*iter));
147
148 leveldb::Status status = db_->Write(write_options, &write_batch);
170 if (!status.ok()) { 149 if (!status.ok()) {
171 // DB::Delete doesn't check if record exists, it returns ok just like 150 // DB::Delete doesn't check if record exists, it returns ok just like
172 // AttachmentStore::Drop should. 151 // AttachmentStore::Drop should.
173 DVLOG(1) << "DB::Delete failed: status=" << status.ToString(); 152 DVLOG(1) << "DB::Write failed: status=" << status.ToString();
174 result_code = UNSPECIFIED_ERROR; 153 result_code = UNSPECIFIED_ERROR;
175 } 154 }
176 } 155 }
177 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code)); 156 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code));
178 } 157 }
179 158
180 AttachmentStore::Result OnDiskAttachmentStore::OpenOrCreate( 159 AttachmentStore::Result OnDiskAttachmentStore::OpenOrCreate(
181 const base::FilePath& path) { 160 const base::FilePath& path) {
182 DCHECK(CalledOnValidThread()); 161 DCHECK(CalledOnValidThread());
183 DCHECK(!db_); 162 DCHECK(!db_);
184 base::FilePath leveldb_path = path.Append(kLeveldbDirectory); 163 base::FilePath leveldb_path = path.Append(kLeveldbDirectory);
185 164
186 leveldb::DB* db_raw; 165 leveldb::DB* db_raw;
187 scoped_ptr<leveldb::DB> db; 166 scoped_ptr<leveldb::DB> db;
188 leveldb::Options options; 167 leveldb::Options options;
189 options.create_if_missing = true; 168 options.create_if_missing = true;
190 // TODO(pavely): crbug/424287 Consider adding info_log, block_cache and 169 // TODO(pavely): crbug/424287 Consider adding info_log, block_cache and
191 // filter_policy to options. 170 // filter_policy to options.
192 leveldb::Status status = 171 leveldb::Status status =
193 leveldb::DB::Open(options, leveldb_path.AsUTF8Unsafe(), &db_raw); 172 leveldb::DB::Open(options, leveldb_path.AsUTF8Unsafe(), &db_raw);
194 if (!status.ok()) { 173 if (!status.ok()) {
195 DVLOG(1) << "DB::Open failed: status=" << status.ToString() 174 DVLOG(1) << "DB::Open failed: status=" << status.ToString()
196 << ", path=" << path.AsUTF8Unsafe(); 175 << ", path=" << path.AsUTF8Unsafe();
197 return UNSPECIFIED_ERROR; 176 return UNSPECIFIED_ERROR;
198 } 177 }
199 178
200 db.reset(db_raw); 179 db.reset(db_raw);
201 180
202 attachment_store_pb::AttachmentStoreMetadata metadata; 181 attachment_store_pb::StoreMetadata metadata;
203 status = ReadStoreMetadata(db.get(), &metadata); 182 status = ReadStoreMetadata(db.get(), &metadata);
204 if (!status.ok() && !status.IsNotFound()) { 183 if (!status.ok() && !status.IsNotFound()) {
205 DVLOG(1) << "ReadStoreMetadata failed: status=" << status.ToString(); 184 DVLOG(1) << "ReadStoreMetadata failed: status=" << status.ToString();
206 return UNSPECIFIED_ERROR; 185 return UNSPECIFIED_ERROR;
207 } 186 }
208 if (status.IsNotFound()) { 187 if (status.IsNotFound()) {
209 // Brand new database. 188 // Brand new database.
210 metadata.set_schema_version(kCurrentSchemaVersion); 189 metadata.set_schema_version(kCurrentSchemaVersion);
211 status = WriteStoreMetadata(db.get(), metadata); 190 status = WriteStoreMetadata(db.get(), metadata);
212 if (!status.ok()) { 191 if (!status.ok()) {
213 DVLOG(1) << "WriteStoreMetadata failed: status=" << status.ToString(); 192 DVLOG(1) << "WriteStoreMetadata failed: status=" << status.ToString();
214 return UNSPECIFIED_ERROR; 193 return UNSPECIFIED_ERROR;
215 } 194 }
216 } 195 }
217 DCHECK(status.ok()); 196 DCHECK(status.ok());
218 197
219 // Upgrade code goes here. 198 // Upgrade code goes here.
220 199
221 if (metadata.schema_version() != kCurrentSchemaVersion) { 200 if (metadata.schema_version() != kCurrentSchemaVersion) {
222 DVLOG(1) << "Unknown schema version: " << metadata.schema_version(); 201 DVLOG(1) << "Unknown schema version: " << metadata.schema_version();
223 return UNSPECIFIED_ERROR; 202 return UNSPECIFIED_ERROR;
224 } 203 }
225 204
226 db_ = db.Pass(); 205 db_ = db.Pass();
227 return SUCCESS; 206 return SUCCESS;
228 } 207 }
229 208
209 scoped_ptr<Attachment> OnDiskAttachmentStore::ReadSingleAttachment(
210 const AttachmentId& attachment_id) {
211 scoped_ptr<Attachment> attachment;
212
213 const std::string key = MakeDataKeyFromAttachmentId(attachment_id);
214 std::string data_str;
215 leveldb::Status status = db_->Get(MakeDataReadOptions(), key, &data_str);
216 if (status.ok()) {
217 scoped_refptr<base::RefCountedMemory> data =
218 base::RefCountedString::TakeString(&data_str);
219 attachment.reset(
220 new Attachment(Attachment::CreateWithId(attachment_id, data)));
221 } else {
222 DVLOG(1) << "DB::Get failed: status=" << status.ToString();
223 }
224 return attachment.Pass();
225 }
226
227 bool OnDiskAttachmentStore::WriteSingleAttachment(
228 const Attachment& attachment) {
229 const std::string metadata_key =
230 MakeMetadataKeyFromAttachmentId(attachment.GetId());
231 const std::string data_key = MakeDataKeyFromAttachmentId(attachment.GetId());
232
233 std::string metadata_str;
234 leveldb::Status status =
235 db_->Get(MakeMetadataReadOptions(), metadata_key, &metadata_str);
236 if (status.ok()) {
237 // Entry exists, don't overwrite.
238 return true;
239 } else if (!status.IsNotFound()) {
240 // Entry exists but failed to read.
241 DVLOG(1) << "DB::Get failed: status=" << status.ToString();
242 return false;
243 }
244 DCHECK(status.IsNotFound());
245
246 leveldb::WriteBatch write_batch;
247 // Write metadata.
248 attachment_store_pb::RecordMetadata metadata;
249 metadata.set_attachment_size(attachment.GetData()->size());
250 metadata_str = metadata.SerializeAsString();
251 write_batch.Put(metadata_key, metadata_str);
252 // Write data.
253 scoped_refptr<base::RefCountedMemory> data = attachment.GetData();
254 leveldb::Slice data_slice(data->front_as<char>(), data->size());
255 write_batch.Put(data_key, data_slice);
256
257 status = db_->Write(MakeWriteOptions(), &write_batch);
258 if (!status.ok()) {
259 // Failed to write.
260 DVLOG(1) << "DB::Write failed: status=" << status.ToString();
261 return false;
262 }
263 return true;
264 }
265
230 std::string OnDiskAttachmentStore::MakeDataKeyFromAttachmentId( 266 std::string OnDiskAttachmentStore::MakeDataKeyFromAttachmentId(
231 const AttachmentId& attachment_id) { 267 const AttachmentId& attachment_id) {
232 std::string key = kDataPrefix + attachment_id.GetProto().unique_id(); 268 std::string key = kDataPrefix + attachment_id.GetProto().unique_id();
233 return key; 269 return key;
234 } 270 }
235 271
272 std::string OnDiskAttachmentStore::MakeMetadataKeyFromAttachmentId(
273 const AttachmentId& attachment_id) {
274 std::string key = kMetadataPrefix + attachment_id.GetProto().unique_id();
275 return key;
276 }
277
236 } // namespace syncer 278 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698