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

Side by Side Diff: chrome/browser/chromeos/contacts/contact_database.cc

Issue 190063004: chromeos: Delete old, unused contacts code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: merge again Created 6 years, 9 months 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "chrome/browser/chromeos/contacts/contact_database.h"
6
7 #include <set>
8
9 #include "base/file_util.h"
10 #include "base/metrics/histogram.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/threading/sequenced_worker_pool.h"
13 #include "chrome/browser/chromeos/contacts/contact.pb.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "third_party/leveldatabase/src/include/leveldb/db.h"
16 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
17 #include "third_party/leveldatabase/src/include/leveldb/options.h"
18 #include "third_party/leveldatabase/src/include/leveldb/slice.h"
19 #include "third_party/leveldatabase/src/include/leveldb/status.h"
20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
21
22 using content::BrowserThread;
23
24 namespace contacts {
25
26 namespace {
27
28 // Initialization results reported via the "Contacts.DatabaseInitResult"
29 // histogram.
30 enum HistogramInitResult {
31 HISTOGRAM_INIT_RESULT_SUCCESS = 0,
32 HISTOGRAM_INIT_RESULT_FAILURE = 1,
33 HISTOGRAM_INIT_RESULT_DELETED_CORRUPTED = 2,
34 HISTOGRAM_INIT_RESULT_MAX_VALUE = 3,
35 };
36
37 // Save results reported via the "Contacts.DatabaseSaveResult" histogram.
38 enum HistogramSaveResult {
39 HISTOGRAM_SAVE_RESULT_SUCCESS = 0,
40 HISTOGRAM_SAVE_RESULT_FAILURE = 1,
41 HISTOGRAM_SAVE_RESULT_MAX_VALUE = 2,
42 };
43
44 // Load results reported via the "Contacts.DatabaseLoadResult" histogram.
45 enum HistogramLoadResult {
46 HISTOGRAM_LOAD_RESULT_SUCCESS = 0,
47 HISTOGRAM_LOAD_RESULT_METADATA_PARSE_FAILURE = 1,
48 HISTOGRAM_LOAD_RESULT_CONTACT_PARSE_FAILURE = 2,
49 HISTOGRAM_LOAD_RESULT_MAX_VALUE = 3,
50 };
51
52 // LevelDB key used for storing UpdateMetadata messages.
53 const char kUpdateMetadataKey[] = "__chrome_update_metadata__";
54
55 } // namespace
56
57 ContactDatabase::ContactDatabase()
58 : weak_ptr_factory_(this) {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
60 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
61 task_runner_ = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
62 }
63
64 void ContactDatabase::DestroyOnUIThread() {
65 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
66 weak_ptr_factory_.InvalidateWeakPtrs();
67 task_runner_->PostNonNestableTask(
68 FROM_HERE,
69 base::Bind(&ContactDatabase::DestroyFromTaskRunner,
70 base::Unretained(this)));
71 }
72
73 void ContactDatabase::Init(const base::FilePath& database_dir,
74 InitCallback callback) {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
76 bool* success = new bool(false);
77 task_runner_->PostTaskAndReply(
78 FROM_HERE,
79 base::Bind(&ContactDatabase::InitFromTaskRunner,
80 base::Unretained(this),
81 database_dir,
82 success),
83 base::Bind(&ContactDatabase::RunInitCallback,
84 weak_ptr_factory_.GetWeakPtr(),
85 callback,
86 base::Owned(success)));
87 }
88
89 void ContactDatabase::SaveContacts(scoped_ptr<ContactPointers> contacts_to_save,
90 scoped_ptr<ContactIds> contact_ids_to_delete,
91 scoped_ptr<UpdateMetadata> metadata,
92 bool is_full_update,
93 SaveCallback callback) {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
95 bool* success = new bool(false);
96 task_runner_->PostTaskAndReply(
97 FROM_HERE,
98 base::Bind(&ContactDatabase::SaveContactsFromTaskRunner,
99 base::Unretained(this),
100 base::Passed(&contacts_to_save),
101 base::Passed(&contact_ids_to_delete),
102 base::Passed(&metadata),
103 is_full_update,
104 success),
105 base::Bind(&ContactDatabase::RunSaveCallback,
106 weak_ptr_factory_.GetWeakPtr(),
107 callback,
108 base::Owned(success)));
109 }
110
111 void ContactDatabase::LoadContacts(LoadCallback callback) {
112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
113
114 bool* success = new bool(false);
115 scoped_ptr<ScopedVector<Contact> > contacts(new ScopedVector<Contact>);
116 scoped_ptr<UpdateMetadata> metadata(new UpdateMetadata);
117
118 // Extract pointers before we calling Pass() so we can use them below.
119 ScopedVector<Contact>* contacts_ptr = contacts.get();
120 UpdateMetadata* metadata_ptr = metadata.get();
121
122 task_runner_->PostTaskAndReply(
123 FROM_HERE,
124 base::Bind(&ContactDatabase::LoadContactsFromTaskRunner,
125 base::Unretained(this),
126 success,
127 contacts_ptr,
128 metadata_ptr),
129 base::Bind(&ContactDatabase::RunLoadCallback,
130 weak_ptr_factory_.GetWeakPtr(),
131 callback,
132 base::Owned(success),
133 base::Passed(&contacts),
134 base::Passed(&metadata)));
135 }
136
137 ContactDatabase::~ContactDatabase() {
138 DCHECK(IsRunByTaskRunner());
139 }
140
141 bool ContactDatabase::IsRunByTaskRunner() const {
142 return BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread();
143 }
144
145 void ContactDatabase::DestroyFromTaskRunner() {
146 DCHECK(IsRunByTaskRunner());
147 delete this;
148 }
149
150 void ContactDatabase::RunInitCallback(InitCallback callback,
151 const bool* success) {
152 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
153 callback.Run(*success);
154 }
155
156 void ContactDatabase::RunSaveCallback(SaveCallback callback,
157 const bool* success) {
158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159 callback.Run(*success);
160 }
161
162 void ContactDatabase::RunLoadCallback(
163 LoadCallback callback,
164 const bool* success,
165 scoped_ptr<ScopedVector<Contact> > contacts,
166 scoped_ptr<UpdateMetadata> metadata) {
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
168 callback.Run(*success, contacts.Pass(), metadata.Pass());
169 }
170
171 void ContactDatabase::InitFromTaskRunner(const base::FilePath& database_dir,
172 bool* success) {
173 DCHECK(IsRunByTaskRunner());
174 DCHECK(success);
175
176 VLOG(1) << "Opening " << database_dir.value();
177 UMA_HISTOGRAM_MEMORY_KB("Contacts.DatabaseSizeBytes",
178 base::ComputeDirectorySize(database_dir));
179 *success = false;
180 HistogramInitResult histogram_result = HISTOGRAM_INIT_RESULT_SUCCESS;
181
182 leveldb::Options options;
183 options.create_if_missing = true;
184 options.max_open_files = 0; // Use minimum.
185 bool delete_and_retry_on_corruption = true;
186
187 while (true) {
188 leveldb::DB* db = NULL;
189 leveldb::Status status =
190 leveldb::DB::Open(options, database_dir.value(), &db);
191 if (status.ok()) {
192 CHECK(db);
193 db_.reset(db);
194 *success = true;
195 return;
196 }
197
198 LOG(WARNING) << "Unable to open " << database_dir.value() << ": "
199 << status.ToString();
200
201 // Delete the existing database and try again (just once, though).
202 if (status.IsCorruption() && delete_and_retry_on_corruption) {
203 LOG(WARNING) << "Deleting possibly-corrupt database";
204 base::DeleteFile(database_dir, true);
205 delete_and_retry_on_corruption = false;
206 histogram_result = HISTOGRAM_INIT_RESULT_DELETED_CORRUPTED;
207 } else {
208 histogram_result = HISTOGRAM_INIT_RESULT_FAILURE;
209 break;
210 }
211 }
212
213 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseInitResult",
214 histogram_result,
215 HISTOGRAM_INIT_RESULT_MAX_VALUE);
216 }
217
218 void ContactDatabase::SaveContactsFromTaskRunner(
219 scoped_ptr<ContactPointers> contacts_to_save,
220 scoped_ptr<ContactIds> contact_ids_to_delete,
221 scoped_ptr<UpdateMetadata> metadata,
222 bool is_full_update,
223 bool* success) {
224 DCHECK(IsRunByTaskRunner());
225 DCHECK(success);
226 VLOG(1) << "Saving " << contacts_to_save->size() << " contact(s) to database "
227 << "and deleting " << contact_ids_to_delete->size() << " as "
228 << (is_full_update ? "full" : "incremental") << " update";
229
230 *success = false;
231
232 // If we're doing a full update, find all of the existing keys first so we can
233 // delete ones that aren't present in the new set of contacts.
234 std::set<std::string> keys_to_delete;
235 if (is_full_update) {
236 leveldb::ReadOptions options;
237 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
238 db_iterator->SeekToFirst();
239 while (db_iterator->Valid()) {
240 std::string key = db_iterator->key().ToString();
241 if (key != kUpdateMetadataKey)
242 keys_to_delete.insert(key);
243 db_iterator->Next();
244 }
245 } else {
246 for (ContactIds::const_iterator it = contact_ids_to_delete->begin();
247 it != contact_ids_to_delete->end(); ++it) {
248 keys_to_delete.insert(*it);
249 }
250 }
251
252 // TODO(derat): Serializing all of the contacts and so we can write them in a
253 // single batch may be expensive, memory-wise. Consider writing them in
254 // several batches instead. (To avoid using partial writes in the event of a
255 // crash, maybe add a dummy "write completed" contact that's removed in the
256 // first batch and added in the last.)
257 leveldb::WriteBatch updates;
258 for (ContactPointers::const_iterator it = contacts_to_save->begin();
259 it != contacts_to_save->end(); ++it) {
260 const contacts::Contact& contact = **it;
261 if (contact.contact_id() == kUpdateMetadataKey) {
262 LOG(WARNING) << "Skipping contact with reserved ID "
263 << contact.contact_id();
264 continue;
265 }
266 updates.Put(leveldb::Slice(contact.contact_id()),
267 leveldb::Slice(contact.SerializeAsString()));
268 if (is_full_update)
269 keys_to_delete.erase(contact.contact_id());
270 }
271
272 for (std::set<std::string>::const_iterator it = keys_to_delete.begin();
273 it != keys_to_delete.end(); ++it) {
274 updates.Delete(leveldb::Slice(*it));
275 }
276
277 updates.Put(leveldb::Slice(kUpdateMetadataKey),
278 leveldb::Slice(metadata->SerializeAsString()));
279
280 leveldb::WriteOptions options;
281 options.sync = true;
282 leveldb::Status status = db_->Write(options, &updates);
283 if (status.ok())
284 *success = true;
285 else
286 LOG(WARNING) << "Failed writing contacts: " << status.ToString();
287
288 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseSaveResult",
289 *success ?
290 HISTOGRAM_SAVE_RESULT_SUCCESS :
291 HISTOGRAM_SAVE_RESULT_FAILURE,
292 HISTOGRAM_SAVE_RESULT_MAX_VALUE);
293 }
294
295 void ContactDatabase::LoadContactsFromTaskRunner(
296 bool* success,
297 ScopedVector<Contact>* contacts,
298 UpdateMetadata* metadata) {
299 DCHECK(IsRunByTaskRunner());
300 DCHECK(success);
301 DCHECK(contacts);
302 DCHECK(metadata);
303
304 *success = false;
305 contacts->clear();
306 metadata->Clear();
307
308 leveldb::ReadOptions options;
309 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
310 db_iterator->SeekToFirst();
311 while (db_iterator->Valid()) {
312 leveldb::Slice value_slice = db_iterator->value();
313
314 if (db_iterator->key().ToString() == kUpdateMetadataKey) {
315 if (!metadata->ParseFromArray(value_slice.data(), value_slice.size())) {
316 LOG(WARNING) << "Unable to parse metadata";
317 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseLoadResult",
318 HISTOGRAM_LOAD_RESULT_METADATA_PARSE_FAILURE,
319 HISTOGRAM_LOAD_RESULT_MAX_VALUE);
320 return;
321 }
322 } else {
323 scoped_ptr<Contact> contact(new Contact);
324 if (!contact->ParseFromArray(value_slice.data(), value_slice.size())) {
325 LOG(WARNING) << "Unable to parse contact "
326 << db_iterator->key().ToString();
327 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseLoadResult",
328 HISTOGRAM_LOAD_RESULT_CONTACT_PARSE_FAILURE,
329 HISTOGRAM_LOAD_RESULT_MAX_VALUE);
330 return;
331 }
332 contacts->push_back(contact.release());
333 }
334 db_iterator->Next();
335 }
336
337 *success = true;
338 UMA_HISTOGRAM_ENUMERATION("Contacts.DatabaseLoadResult",
339 HISTOGRAM_LOAD_RESULT_SUCCESS,
340 HISTOGRAM_LOAD_RESULT_MAX_VALUE);
341 }
342
343 } // namespace contacts
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/contacts/contact_database.h ('k') | chrome/browser/chromeos/contacts/contact_database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698