Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "sync/internal_api/public/attachments/on_disk_attachment_store.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback.h" | |
| 9 #include "base/location.h" | |
| 10 #include "base/memory/scoped_ptr.h" | |
| 11 #include "base/sequenced_task_runner.h" | |
| 12 #include "sync/protocol/attachments.pb.h" | |
| 13 #include "third_party/leveldatabase/src/include/leveldb/db.h" | |
| 14 #include "third_party/leveldatabase/src/include/leveldb/options.h" | |
| 15 #include "third_party/leveldatabase/src/include/leveldb/slice.h" | |
| 16 #include "third_party/leveldatabase/src/include/leveldb/status.h" | |
| 17 | |
| 18 namespace syncer { | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // Prefix for records containing attachment data. | |
| 23 const char kDataPrefix[] = "data-"; | |
| 24 | |
| 25 const base::FilePath::CharType kLeveldbDirectory[] = | |
| 26 FILE_PATH_LITERAL("leveldb"); | |
| 27 } // namespace | |
| 28 | |
| 29 OnDiskAttachmentStore::OnDiskAttachmentStore( | |
| 30 const scoped_refptr<base::SequencedTaskRunner>& callback_task_runner) | |
| 31 : callback_task_runner_(callback_task_runner) { | |
| 32 } | |
| 33 | |
| 34 OnDiskAttachmentStore::~OnDiskAttachmentStore() { | |
| 35 } | |
| 36 | |
| 37 void OnDiskAttachmentStore::Read(const AttachmentIdList& ids, | |
| 38 const ReadCallback& callback) { | |
| 39 DCHECK(CalledOnValidThread()); | |
| 40 DCHECK(db_); | |
| 41 scoped_ptr<AttachmentMap> result_map(new AttachmentMap()); | |
| 42 scoped_ptr<AttachmentIdList> unavailable_attachments(new AttachmentIdList()); | |
| 43 | |
| 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 | |
| 50 AttachmentIdList::const_iterator iter = ids.begin(); | |
| 51 const AttachmentIdList::const_iterator end = ids.end(); | |
| 52 for (; iter != end; ++iter) { | |
| 53 std::string key = CreateDataKeyFromAttachmentId(*iter); | |
|
maniscalco
2014/10/17 00:00:19
Opportunity for const.
pavely
2014/10/17 00:13:25
Done.
| |
| 54 std::string data_str; | |
| 55 leveldb::Status status = db_->Get(read_options, key, &data_str); | |
| 56 if (!status.ok()) { | |
| 57 DVLOG(1) << "DB::Get failed: status=" << status.ToString(); | |
| 58 unavailable_attachments->push_back(*iter); | |
| 59 continue; | |
| 60 } | |
| 61 scoped_refptr<base::RefCountedMemory> data = | |
| 62 base::RefCountedString::TakeString(&data_str); | |
| 63 Attachment attachment = Attachment::CreateWithId(*iter, data); | |
| 64 result_map->insert(std::make_pair(*iter, attachment)); | |
| 65 } | |
| 66 | |
| 67 Result result_code = | |
| 68 unavailable_attachments->empty() ? SUCCESS : UNSPECIFIED_ERROR; | |
| 69 callback_task_runner_->PostTask( | |
| 70 FROM_HERE, | |
| 71 base::Bind(callback, | |
| 72 result_code, | |
| 73 base::Passed(&result_map), | |
| 74 base::Passed(&unavailable_attachments))); | |
| 75 } | |
| 76 | |
| 77 void OnDiskAttachmentStore::Write(const AttachmentList& attachments, | |
| 78 const WriteCallback& callback) { | |
| 79 DCHECK(CalledOnValidThread()); | |
| 80 DCHECK(db_); | |
| 81 Result result_code = SUCCESS; | |
| 82 | |
| 83 leveldb::ReadOptions read_options; | |
| 84 read_options.fill_cache = false; | |
| 85 read_options.verify_checksums = true; | |
| 86 | |
| 87 leveldb::WriteOptions write_options; | |
| 88 write_options.sync = true; | |
| 89 | |
| 90 AttachmentList::const_iterator iter = attachments.begin(); | |
| 91 const AttachmentList::const_iterator end = attachments.end(); | |
| 92 for (; iter != end; ++iter) { | |
|
maniscalco
2014/10/17 00:00:19
Opportunity for const. I often wish automatic var
pavely
2014/10/17 00:13:25
Done.
| |
| 93 std::string key = CreateDataKeyFromAttachmentId(iter->GetId()); | |
| 94 | |
| 95 std::string data_str; | |
| 96 // TODO(pavely): crbug/424304 This read is expensive. When I add metadata | |
| 97 // records this read will target metadata record instead of payload record. | |
| 98 leveldb::Status status = db_->Get(read_options, key, &data_str); | |
| 99 if (status.ok()) { | |
| 100 // Entry exists, don't overwrite. | |
| 101 continue; | |
| 102 } else if (!status.IsNotFound()) { | |
| 103 // Entry exists but failed to read. | |
| 104 DVLOG(1) << "DB::Get failed: status=" << status.ToString(); | |
| 105 result_code = UNSPECIFIED_ERROR; | |
| 106 continue; | |
| 107 } | |
| 108 DCHECK(status.IsNotFound()); | |
| 109 | |
| 110 scoped_refptr<base::RefCountedMemory> data = iter->GetData(); | |
| 111 leveldb::Slice data_slice(data->front_as<char>(), data->size()); | |
| 112 status = db_->Put(write_options, key, data_slice); | |
| 113 if (!status.ok()) { | |
| 114 // Failed to write. | |
| 115 DVLOG(1) << "DB::Put failed: status=" << status.ToString(); | |
| 116 result_code = UNSPECIFIED_ERROR; | |
| 117 } | |
| 118 } | |
| 119 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code)); | |
| 120 } | |
| 121 | |
| 122 void OnDiskAttachmentStore::Drop(const AttachmentIdList& ids, | |
| 123 const DropCallback& callback) { | |
| 124 DCHECK(CalledOnValidThread()); | |
| 125 DCHECK(db_); | |
| 126 Result result_code = SUCCESS; | |
| 127 leveldb::WriteOptions write_options; | |
| 128 write_options.sync = true; | |
| 129 AttachmentIdList::const_iterator iter = ids.begin(); | |
| 130 const AttachmentIdList::const_iterator end = ids.end(); | |
| 131 for (; iter != end; ++iter) { | |
| 132 std::string key = CreateDataKeyFromAttachmentId(*iter); | |
| 133 leveldb::Status status = db_->Delete(write_options, key); | |
| 134 if (!status.ok()) { | |
| 135 // DB::Delete doesn't check if record exists, it returns ok just like | |
| 136 // AttachmentStore::Drop should. | |
| 137 DVLOG(1) << "DB::Delete failed: status=" << status.ToString(); | |
| 138 result_code = UNSPECIFIED_ERROR; | |
| 139 } | |
| 140 } | |
| 141 callback_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result_code)); | |
| 142 } | |
| 143 | |
| 144 AttachmentStore::Result OnDiskAttachmentStore::OpenOrCreate( | |
| 145 const base::FilePath& path) { | |
| 146 DCHECK(CalledOnValidThread()); | |
| 147 DCHECK(!db_); | |
| 148 Result result_code = UNSPECIFIED_ERROR; | |
| 149 base::FilePath leveldb_path = path.Append(kLeveldbDirectory); | |
| 150 | |
| 151 leveldb::DB* db; | |
| 152 leveldb::Options options; | |
| 153 options.create_if_missing = true; | |
| 154 // TODO(pavely): crbug/424287 Consider adding info_log, block_cache and | |
| 155 // filter_policy to options. | |
| 156 leveldb::Status status = | |
| 157 leveldb::DB::Open(options, leveldb_path.AsUTF8Unsafe(), &db); | |
| 158 if (!status.ok()) { | |
| 159 DVLOG(1) << "DB::Open failed: status=" << status.ToString() | |
| 160 << ", path=" << path.AsUTF8Unsafe(); | |
| 161 } else { | |
| 162 db_.reset(db); | |
| 163 result_code = SUCCESS; | |
| 164 } | |
| 165 return result_code; | |
| 166 } | |
| 167 | |
| 168 std::string OnDiskAttachmentStore::CreateDataKeyFromAttachmentId( | |
| 169 const AttachmentId& attachment_id) { | |
| 170 std::string key = kDataPrefix + attachment_id.GetProto().unique_id(); | |
| 171 return key; | |
| 172 } | |
| 173 | |
| 174 } // namespace syncer | |
| OLD | NEW |