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

Side by Side Diff: content/browser/indexed_db/indexed_db_backing_store.cc

Issue 264483002: This is the implementation of the primary and secondary blob journals for (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: formatting, cleanup, removed a merge error Created 6 years, 7 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
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 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 "content/browser/indexed_db/indexed_db_backing_store.h" 5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
6 6
7 #include "base/file_util.h" 7 #include "base/file_util.h"
8 #include "base/files/file_path.h" 8 #include "base/files/file_path.h"
9 #include "base/format_macros.h" 9 #include "base/format_macros.h"
10 #include "base/json/json_reader.h" 10 #include "base/json/json_reader.h"
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 SET_MAX_OBJECT_STORE_ID, 119 SET_MAX_OBJECT_STORE_ID,
120 SET_MAX_INDEX_ID, 120 SET_MAX_INDEX_ID,
121 GET_NEW_DATABASE_ID, 121 GET_NEW_DATABASE_ID,
122 GET_NEW_VERSION_NUMBER, 122 GET_NEW_VERSION_NUMBER,
123 CREATE_IDBDATABASE_METADATA, 123 CREATE_IDBDATABASE_METADATA,
124 DELETE_DATABASE, 124 DELETE_DATABASE,
125 TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro 125 TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro
126 GET_DATABASE_NAMES, 126 GET_DATABASE_NAMES,
127 DELETE_INDEX, 127 DELETE_INDEX,
128 CLEAR_OBJECT_STORE, 128 CLEAR_OBJECT_STORE,
129 DECODE_BLOB_JOURNAL,
129 INTERNAL_ERROR_MAX, 130 INTERNAL_ERROR_MAX,
130 }; 131 };
131 132
132 static void RecordInternalError(const char* type, 133 static void RecordInternalError(const char* type,
133 IndexedDBBackingStoreErrorSource location) { 134 IndexedDBBackingStoreErrorSource location) {
134 std::string name; 135 std::string name;
135 name.append("WebCore.IndexedDB.BackingStore.").append(type).append("Error"); 136 name.append("WebCore.IndexedDB.BackingStore.").append(type).append("Error");
136 base::Histogram::FactoryGet(name, 137 base::Histogram::FactoryGet(name,
137 1, 138 1,
138 INTERNAL_ERROR_MAX, 139 INTERNAL_ERROR_MAX,
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after
467 scoped_ptr<LevelDBDatabase>* db, 468 scoped_ptr<LevelDBDatabase>* db,
468 bool* is_disk_full) OVERRIDE { 469 bool* is_disk_full) OVERRIDE {
469 return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full); 470 return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
470 } 471 }
471 virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name) 472 virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name)
472 OVERRIDE { 473 OVERRIDE {
473 return LevelDBDatabase::Destroy(file_name); 474 return LevelDBDatabase::Destroy(file_name);
474 } 475 }
475 }; 476 };
476 477
478 // TODO(ericu): Error recovery. If we persistently can't read the
479 // blob journal, the safe thing to do is to clear it and leak the blobs,
480 // though that may be costly. Still, database/directory deletion should always
481 // clean things up, and we can write an fsck that will do a full correction if
482 // need be.
483 template <typename T>
484 static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key,
485 T* leveldb_transaction,
486 BlobJournalType* journal) {
487 std::string data;
488 bool found = false;
489 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
490 if (!s.ok()) {
491 INTERNAL_READ_ERROR(KEY_EXISTS_IN_OBJECT_STORE);
cmumford 2014/04/30 21:29:30 INTERNAL_READ_ERROR_UNTESTED assuming you don't ha
ericu 2014/04/30 22:16:10 I made the quick fix; no test yet.
cmumford 2014/04/30 22:40:09 Unfortunately all corruptions are not detectable a
492 return s;
493 }
494 journal->clear();
495 if (!found)
cmumford 2014/04/30 21:29:30 why not just (if !found || !data.size())?
ericu 2014/04/30 22:16:10 Done.
496 return leveldb::Status::OK();
497 if (!data.size())
498 return leveldb::Status::OK();
499 StringPiece slice(data);
500 if (!DecodeBlobJournal(&slice, journal)) {
501 INTERNAL_READ_ERROR(DECODE_BLOB_JOURNAL);
502 s = InternalInconsistencyStatus();
503 }
504 return s;
505 }
506
507 static void ClearBlobJournal(LevelDBTransaction* leveldb_transaction,
508 const std::string& level_db_key) {
509 leveldb_transaction->Remove(level_db_key);
cmumford 2014/04/30 21:29:30 A one line function doesn't seem worth the added c
ericu 2014/04/30 22:16:10 No, there's just the one line, called in two place
jsbell 2014/04/30 22:21:45 I like abstracting it.
510 }
511
512 static void UpdatePrimaryJournalWithBlobList(
513 LevelDBTransaction* leveldb_transaction,
514 const BlobJournalType& journal) {
515 const std::string leveldb_key = BlobJournalKey::Encode();
516 std::string data;
517 EncodeBlobJournal(journal, &data);
518 leveldb_transaction->Put(leveldb_key, &data);
519 }
520
521 static void UpdateLiveBlobJournalWithBlobList(
522 LevelDBTransaction* leveldb_transaction,
523 const BlobJournalType& journal) {
524 const std::string leveldb_key = LiveBlobJournalKey::Encode();
525 std::string data;
526 EncodeBlobJournal(journal, &data);
527 leveldb_transaction->Put(leveldb_key, &data);
528 }
529
530 static leveldb::Status MergeBlobsIntoLiveBlobJournal(
531 LevelDBTransaction* leveldb_transaction,
532 const BlobJournalType& journal) {
533 BlobJournalType old_journal;
534 std::string key = LiveBlobJournalKey::Encode();
535 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &old_journal);
536 if (!s.ok())
537 return s;
538
539 old_journal.insert(old_journal.end(), journal.begin(), journal.end());
540
541 UpdateLiveBlobJournalWithBlobList(leveldb_transaction, old_journal);
542 return leveldb::Status::OK();
543 }
544
545 static void UpdateBlobJournalWithDatabase(
546 LevelDBDirectTransaction* leveldb_transaction,
547 int64 database_id) {
548 BlobJournalType journal;
549 journal.push_back(
550 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
551 const std::string key = BlobJournalKey::Encode();
552 std::string data;
553 EncodeBlobJournal(journal, &data);
554 leveldb_transaction->Put(key, &data);
555 }
556
557 static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
558 LevelDBDirectTransaction* leveldb_transaction,
559 int64 database_id) {
560 BlobJournalType journal;
561 std::string key = LiveBlobJournalKey::Encode();
cmumford 2014/04/30 21:29:30 nit: I see that sometimes you use a const string f
ericu 2014/04/30 22:16:10 Nope; just an oversight. Fixed.
562 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &journal);
563 if (!s.ok())
564 return s;
565 journal.push_back(
566 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
567 std::string data;
568 EncodeBlobJournal(journal, &data);
569 leveldb_transaction->Put(key, &data);
570 return leveldb::Status::OK();
571 }
572
477 IndexedDBBackingStore::IndexedDBBackingStore( 573 IndexedDBBackingStore::IndexedDBBackingStore(
478 IndexedDBFactory* indexed_db_factory, 574 IndexedDBFactory* indexed_db_factory,
479 const GURL& origin_url, 575 const GURL& origin_url,
480 const base::FilePath& blob_path, 576 const base::FilePath& blob_path,
481 net::URLRequestContext* request_context, 577 net::URLRequestContext* request_context,
482 scoped_ptr<LevelDBDatabase> db, 578 scoped_ptr<LevelDBDatabase> db,
483 scoped_ptr<LevelDBComparator> comparator, 579 scoped_ptr<LevelDBComparator> comparator,
484 base::TaskRunner* task_runner) 580 base::TaskRunner* task_runner)
485 : indexed_db_factory_(indexed_db_factory), 581 : indexed_db_factory_(indexed_db_factory),
486 origin_url_(origin_url), 582 origin_url_(origin_url),
(...skipping 604 matching lines...) Expand 10 before | Expand all | Expand 10 after
1091 s = it->Next()) 1187 s = it->Next())
1092 transaction->Remove(it->Key()); 1188 transaction->Remove(it->Key());
1093 if (!s.ok()) { 1189 if (!s.ok()) {
1094 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE); 1190 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1095 return s; 1191 return s;
1096 } 1192 }
1097 1193
1098 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name); 1194 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1099 transaction->Remove(key); 1195 transaction->Remove(key);
1100 1196
1197 // TODO(ericu): Put the real calls to the blob journal code here. For now,
1198 // I've inserted fake calls so that we don't get "you didn't use this static
1199 // function" compiler errors.
1200 if (false) {
1201 scoped_refptr<LevelDBTransaction> fake_transaction =
1202 new LevelDBTransaction(NULL);
1203 BlobJournalType fake_journal;
1204 MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
1205 UpdateBlobJournalWithDatabase(transaction.get(), metadata.id);
1206 MergeBlobsIntoLiveBlobJournal(fake_transaction.get(), fake_journal);
1207 }
1208
1101 s = transaction->Commit(); 1209 s = transaction->Commit();
1102 if (!s.ok()) { 1210 if (!s.ok()) {
1103 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE); 1211 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1104 return s; 1212 return s;
1105 } 1213 }
1106 db_->Compact(start_key, stop_key); 1214 db_->Compact(start_key, stop_key);
1107 return s; 1215 return s;
1108 } 1216 }
1109 1217
1110 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it, 1218 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
(...skipping 816 matching lines...) Expand 10 before | Expand all | Expand 10 after
1927 FROM_HERE, 2035 FROM_HERE,
1928 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread, 2036 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread,
1929 write_closure.get(), 2037 write_closure.get(),
1930 path, 2038 path,
1931 descriptor.url(), 2039 descriptor.url(),
1932 request_context_)); 2040 request_context_));
1933 } 2041 }
1934 return true; 2042 return true;
1935 } 2043 }
1936 2044
2045 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2046 int64 blob_key) {
2047 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2048 bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2049 DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2050 scoped_refptr<LevelDBTransaction> transaction =
2051 new LevelDBTransaction(db_.get());
2052
2053 std::string live_blob_key = LiveBlobJournalKey::Encode();
2054 BlobJournalType live_blob_journal;
2055 if (!GetBlobJournal(live_blob_key, transaction.get(), &live_blob_journal)
2056 .ok())
2057 return;
2058 DCHECK(live_blob_journal.size());
2059
2060 std::string primary_key = BlobJournalKey::Encode();
2061 BlobJournalType primary_journal;
2062 if (!GetBlobJournal(primary_key, transaction.get(), &primary_journal).ok())
2063 return;
2064
2065 BlobJournalType::iterator journal_iter;
jsbell 2014/04/30 21:18:05 nit: move into the for()'s init clause, since the
ericu 2014/04/30 21:21:14 Yeah, good point.
2066 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2067 // remove all entries with database_id from the live_blob journal and add only
2068 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2069 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2070 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2071 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2072 // matching (database_id, blob_key) tuple, we should move it to the primary
2073 // journal.
2074 BlobJournalType new_live_blob_journal;
2075 for (journal_iter = live_blob_journal.begin();
2076 journal_iter != live_blob_journal.end();
2077 ++journal_iter) {
2078 int64 current_database_id = journal_iter->first;
2079 int64 current_blob_key = journal_iter->second;
2080 bool current_all_blobs =
2081 current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2082 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2083 current_all_blobs);
2084 if (current_database_id == database_id &&
2085 (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2086 if (!all_blobs) {
2087 primary_journal.push_back(
2088 std::make_pair(database_id, current_blob_key));
2089 if (current_all_blobs)
2090 new_live_blob_journal.push_back(*journal_iter);
2091 new_live_blob_journal.insert(new_live_blob_journal.end(),
2092 ++journal_iter,
2093 live_blob_journal.end()); // All the rest.
2094 break;
2095 }
2096 } else {
2097 new_live_blob_journal.push_back(*journal_iter);
2098 }
2099 }
2100 if (all_blobs) {
2101 primary_journal.push_back(
2102 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2103 }
2104 UpdatePrimaryJournalWithBlobList(transaction.get(), primary_journal);
2105 UpdateLiveBlobJournalWithBlobList(transaction.get(), new_live_blob_journal);
2106 transaction->Commit();
2107 // We could just do the deletions/cleaning here, but if there are a lot of
2108 // blobs about to be garbage collected, it'd be better to wait and do them all
2109 // at once.
2110 StartJournalCleaningTimer();
2111 }
2112
2113 // The this reference is a raw pointer that's declared Unretained inside the
2114 // timer code, so this won't confuse IndexedDBFactory's check for
2115 // HasLastBackingStoreReference. It's safe because if the backing store is
2116 // deleted, the timer will automatically be canceled on destruction.
2117 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2118 journal_cleaning_timer_.Start(
2119 FROM_HERE,
2120 base::TimeDelta::FromSeconds(5),
2121 this,
2122 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2123 }
2124
1937 // This assumes a file path of dbId/second-to-LSB-of-counter/counter. 2125 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
1938 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) { 2126 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) {
1939 return GetBlobFileNameForKey(blob_path_, database_id, key); 2127 return GetBlobFileNameForKey(blob_path_, database_id, key);
1940 } 2128 }
1941 2129
1942 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it, 2130 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
1943 const std::string& stop_key, 2131 const std::string& stop_key,
1944 int64 index_id, 2132 int64 index_id,
1945 unsigned char meta_data_type) { 2133 unsigned char meta_data_type) {
1946 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0) 2134 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
2058 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) { 2246 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) {
2059 FilePath fileName = GetBlobFileName(database_id, key); 2247 FilePath fileName = GetBlobFileName(database_id, key);
2060 return base::DeleteFile(fileName, false); 2248 return base::DeleteFile(fileName, false);
2061 } 2249 }
2062 2250
2063 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) { 2251 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) {
2064 FilePath dirName = GetBlobDirectoryName(blob_path_, database_id); 2252 FilePath dirName = GetBlobDirectoryName(blob_path_, database_id);
2065 return base::DeleteFile(dirName, true); 2253 return base::DeleteFile(dirName, true);
2066 } 2254 }
2067 2255
2256 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2257 const std::string& level_db_key) {
2258 scoped_refptr<LevelDBTransaction> journal_transaction =
2259 new LevelDBTransaction(db_.get());
2260 BlobJournalType journal;
2261 leveldb::Status s =
2262 GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2263 if (!s.ok())
2264 return s;
2265 if (!journal.size())
2266 return leveldb::Status::OK();
cmumford 2014/04/30 21:29:30 No need to worry about journal_transaction->Commit
ericu 2014/04/30 22:16:10 I'm not sure what you mean. It's discarded on exi
cmumford 2014/04/30 22:32:33 I misread it - thought transaction was a param - m
2267 BlobJournalType::iterator journal_iter;
2268 for (journal_iter = journal.begin(); journal_iter != journal.end();
2269 ++journal_iter) {
2270 int64 database_id = journal_iter->first;
2271 int64 blob_key = journal_iter->second;
2272 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2273 if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2274 RemoveBlobDirectory(database_id);
2275 } else {
2276 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2277 RemoveBlobFile(database_id, blob_key);
cmumford 2014/04/30 21:29:30 Since this returns false on error can we at least
ericu 2014/04/30 22:16:10 Actually, we should do more than that. Changed to
2278 }
2279 }
2280 ClearBlobJournal(journal_transaction.get(), level_db_key);
2281 return journal_transaction->Commit();
2282 }
2283
2284 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2285 CleanUpBlobJournal(BlobJournalKey::Encode());
2286 }
2287
2068 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId( 2288 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2069 LevelDBTransaction* transaction, 2289 LevelDBTransaction* transaction,
2070 int64 database_id, 2290 int64 database_id,
2071 int64 object_store_id, 2291 int64 object_store_id,
2072 int64 index_id) { 2292 int64 index_id) {
2073 int64 max_index_id = -1; 2293 int64 max_index_id = -1;
2074 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode( 2294 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2075 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID); 2295 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2076 bool found = false; 2296 bool found = false;
2077 leveldb::Status s = 2297 leveldb::Status s =
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after
2375 return InvalidDBKeyStatus(); 2595 return InvalidDBKeyStatus();
2376 } 2596 }
2377 2597
2378 StringPiece slice(found_encoded_primary_key); 2598 StringPiece slice(found_encoded_primary_key);
2379 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty()) 2599 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
2380 return s; 2600 return s;
2381 else 2601 else
2382 return InvalidDBKeyStatus(); 2602 return InvalidDBKeyStatus();
2383 } 2603 }
2384 2604
2385 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2386 int64 blob_key) {
2387 // TODO(ericu)
2388 }
2389
2390 IndexedDBBackingStore::Cursor::Cursor( 2605 IndexedDBBackingStore::Cursor::Cursor(
2391 const IndexedDBBackingStore::Cursor* other) 2606 const IndexedDBBackingStore::Cursor* other)
2392 : transaction_(other->transaction_), 2607 : transaction_(other->transaction_),
2393 cursor_options_(other->cursor_options_), 2608 cursor_options_(other->cursor_options_),
2394 current_key_(new IndexedDBKey(*other->current_key_)) { 2609 current_key_(new IndexedDBKey(*other->current_key_)) {
2395 if (other->iterator_) { 2610 if (other->iterator_) {
2396 iterator_ = transaction_->CreateIterator(); 2611 iterator_ = transaction_->CreateIterator();
2397 2612
2398 if (other->iterator_->IsValid()) { 2613 if (other->iterator_->IsValid()) {
2399 leveldb::Status s = iterator_->Seek(other->iterator_->Key()); 2614 leveldb::Status s = iterator_->Seek(other->iterator_->Key());
(...skipping 914 matching lines...) Expand 10 before | Expand all | Expand 10 after
3314 const GURL& url, 3529 const GURL& url,
3315 int64_t key) 3530 int64_t key)
3316 : is_file_(false), url_(url), key_(key) {} 3531 : is_file_(false), url_(url), key_(key) {}
3317 3532
3318 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor( 3533 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
3319 const FilePath& file_path, 3534 const FilePath& file_path,
3320 int64_t key) 3535 int64_t key)
3321 : is_file_(true), file_path_(file_path), key_(key) {} 3536 : is_file_(true), file_path_(file_path), key_(key) {}
3322 3537
3323 } // namespace content 3538 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698