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 |