Index: content/browser/indexed_db/indexed_db_backing_store.cc |
diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc |
index 3ec2ab2f129bb9f052f65512009333bd49deb41e..b9faa35418da1f4f81c7335bcda1985ce1b7c5b6 100644 |
--- a/content/browser/indexed_db/indexed_db_backing_store.cc |
+++ b/content/browser/indexed_db/indexed_db_backing_store.cc |
@@ -4,6 +4,8 @@ |
#include "content/browser/indexed_db/indexed_db_backing_store.h" |
+#include <algorithm> |
+ |
#include "base/files/file_path.h" |
#include "base/files/file_util.h" |
#include "base/format_macros.h" |
@@ -556,13 +558,16 @@ static bool UpdateBlobKeyGeneratorCurrentNumber( |
// though that may be costly. Still, database/directory deletion should always |
// clean things up, and we can write an fsck that will do a full correction if |
// need be. |
-template <typename T> |
-static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key, |
- T* leveldb_transaction, |
+ |
+// Read and decode the specified blob journal via the supplied transaction. |
+// The key must be either the primary journal key or live journal key. |
+template <typename TransactionType> |
+static leveldb::Status GetBlobJournal(const StringPiece& key, |
+ TransactionType* transaction, |
BlobJournalType* journal) { |
std::string data; |
bool found = false; |
- leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found); |
+ leveldb::Status s = transaction->Get(key, &data, &found); |
if (!s.ok()) { |
INTERNAL_READ_ERROR(READ_BLOB_JOURNAL); |
return s; |
@@ -578,72 +583,114 @@ static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key, |
return s; |
} |
-static void ClearBlobJournal(LevelDBTransaction* leveldb_transaction, |
- const std::string& level_db_key) { |
- leveldb_transaction->Remove(level_db_key); |
+template <typename TransactionType> |
+static leveldb::Status GetPrimaryBlobJournal(TransactionType* transaction, |
+ BlobJournalType* journal) { |
+ return GetBlobJournal(BlobJournalKey::Encode(), transaction, journal); |
} |
-static void UpdatePrimaryJournalWithBlobList( |
- LevelDBTransaction* leveldb_transaction, |
- const BlobJournalType& journal) { |
- const std::string leveldb_key = BlobJournalKey::Encode(); |
- std::string data; |
- EncodeBlobJournal(journal, &data); |
- leveldb_transaction->Put(leveldb_key, &data); |
+template <typename TransactionType> |
+static leveldb::Status GetLiveBlobJournal(TransactionType* transaction, |
+ BlobJournalType* journal) { |
+ return GetBlobJournal(LiveBlobJournalKey::Encode(), transaction, journal); |
} |
-static void UpdateLiveBlobJournalWithBlobList( |
- LevelDBTransaction* leveldb_transaction, |
- const BlobJournalType& journal) { |
- const std::string leveldb_key = LiveBlobJournalKey::Encode(); |
+// Clear the specified blob journal via the supplied transaction. |
+// The key must be either the primary journal key or live journal key. |
+template <typename TransactionType> |
+static void ClearBlobJournal(TransactionType* transaction, |
+ const std::string& key) { |
+ transaction->Remove(key); |
+} |
+ |
+// Overwrite the specified blob journal via the supplied transaction. |
+// The key must be either the primary journal key or live journal key. |
+template <typename TransactionType> |
+static void UpdateBlobJournal(TransactionType* transaction, |
+ const std::string& key, |
+ const BlobJournalType& journal) { |
std::string data; |
EncodeBlobJournal(journal, &data); |
- leveldb_transaction->Put(leveldb_key, &data); |
+ transaction->Put(key, &data); |
} |
-static leveldb::Status MergeBlobsIntoLiveBlobJournal( |
- LevelDBTransaction* leveldb_transaction, |
+template <typename TransactionType> |
+static void UpdatePrimaryBlobJournal(TransactionType* transaction, |
+ const BlobJournalType& journal) { |
+ UpdateBlobJournal(transaction, BlobJournalKey::Encode(), journal); |
+} |
+ |
+template <typename TransactionType> |
+static void UpdateLiveBlobJournal(TransactionType* transaction, |
+ const BlobJournalType& journal) { |
+ UpdateBlobJournal(transaction, LiveBlobJournalKey::Encode(), journal); |
+} |
+ |
+// Append blobs to the specified blob journal via the supplied transaction. |
+// The key must be either the primary journal key or live journal key. |
+template <typename TransactionType> |
+static leveldb::Status AppendBlobsToBlobJournal( |
+ TransactionType* transaction, |
+ const std::string& key, |
const BlobJournalType& journal) { |
+ if (!journal.size()) |
cmumford
2015/01/22 00:56:57
Nit: if (journal.empty()) reads much better.
jsbell
2015/01/22 19:40:45
Fixed here and other blob places.
|
+ return leveldb::Status::OK(); |
BlobJournalType old_journal; |
- const std::string key = LiveBlobJournalKey::Encode(); |
- leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &old_journal); |
+ leveldb::Status s = GetBlobJournal(key, transaction, &old_journal); |
if (!s.ok()) |
return s; |
- |
old_journal.insert(old_journal.end(), journal.begin(), journal.end()); |
- |
- UpdateLiveBlobJournalWithBlobList(leveldb_transaction, old_journal); |
+ UpdateBlobJournal(transaction, key, old_journal); |
return leveldb::Status::OK(); |
} |
-static void UpdateBlobJournalWithDatabase( |
- LevelDBDirectTransaction* leveldb_transaction, |
- int64 database_id) { |
- BlobJournalType journal; |
- journal.push_back( |
- std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey)); |
- const std::string key = BlobJournalKey::Encode(); |
- std::string data; |
- EncodeBlobJournal(journal, &data); |
- leveldb_transaction->Put(key, &data); |
+template <typename TransactionType> |
+static leveldb::Status AppendBlobsToPrimaryBlobJournal( |
+ TransactionType* transaction, |
+ const BlobJournalType& journal) { |
+ return AppendBlobsToBlobJournal(transaction, BlobJournalKey::Encode(), |
+ journal); |
} |
-static leveldb::Status MergeDatabaseIntoLiveBlobJournal( |
- LevelDBDirectTransaction* leveldb_transaction, |
+template <typename TransactionType> |
+static leveldb::Status AppendBlobsToLiveBlobJournal( |
+ TransactionType* transaction, |
+ const BlobJournalType& journal) { |
+ BlobJournalType old_journal; |
cmumford
2015/01/22 00:56:57
Do we still need "old_journal"?
jsbell
2015/01/22 19:40:45
Nope - removed.
|
+ return AppendBlobsToBlobJournal(transaction, LiveBlobJournalKey::Encode(), |
+ journal); |
+} |
+ |
+// Append a database to the specified blob journal via the supplied transaction. |
+// The key must be either the primary journal key or live journal key. |
+static leveldb::Status MergeDatabaseIntoBlobJournal( |
+ LevelDBDirectTransaction* transaction, |
+ const std::string& key, |
int64 database_id) { |
BlobJournalType journal; |
- const std::string key = LiveBlobJournalKey::Encode(); |
- leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &journal); |
+ leveldb::Status s = GetBlobJournal(key, transaction, &journal); |
if (!s.ok()) |
return s; |
journal.push_back( |
std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey)); |
- std::string data; |
- EncodeBlobJournal(journal, &data); |
- leveldb_transaction->Put(key, &data); |
+ UpdateBlobJournal(transaction, key, journal); |
return leveldb::Status::OK(); |
} |
+static leveldb::Status MergeDatabaseIntoPrimaryBlobJournal( |
+ LevelDBDirectTransaction* leveldb_transaction, |
+ int64 database_id) { |
+ return MergeDatabaseIntoBlobJournal(leveldb_transaction, |
+ BlobJournalKey::Encode(), database_id); |
+} |
+ |
+static leveldb::Status MergeDatabaseIntoLiveBlobJournal( |
+ LevelDBDirectTransaction* leveldb_transaction, |
+ int64 database_id) { |
+ return MergeDatabaseIntoBlobJournal( |
+ leveldb_transaction, LiveBlobJournalKey::Encode(), database_id); |
+} |
+ |
// Blob Data is encoded as a series of: |
// { is_file [bool], key [int64 as varInt], |
// type [string-with-length, may be empty], |
@@ -716,7 +763,8 @@ IndexedDBBackingStore::IndexedDBBackingStore( |
task_runner_(task_runner), |
db_(db.Pass()), |
comparator_(comparator.Pass()), |
- active_blob_registry_(this) { |
+ active_blob_registry_(this), |
+ committing_transaction_count_(0) { |
} |
IndexedDBBackingStore::~IndexedDBBackingStore() { |
@@ -1414,9 +1462,6 @@ leveldb::Status IndexedDBBackingStore::DeleteDatabase( |
LevelDBDirectTransaction::Create(db_.get()); |
leveldb::Status s; |
- s = CleanUpBlobJournal(BlobJournalKey::Encode()); |
- if (!s.ok()) |
- return s; |
IndexedDBDatabaseMetadata metadata; |
bool success = false; |
@@ -1450,7 +1495,9 @@ leveldb::Status IndexedDBBackingStore::DeleteDatabase( |
if (!s.ok()) |
return s; |
} else { |
- UpdateBlobJournalWithDatabase(transaction.get(), metadata.id); |
+ s = MergeDatabaseIntoPrimaryBlobJournal(transaction.get(), metadata.id); |
+ if (!s.ok()) |
+ return s; |
need_cleanup = true; |
} |
@@ -1460,8 +1507,10 @@ leveldb::Status IndexedDBBackingStore::DeleteDatabase( |
return s; |
} |
+ // If another transaction is running, this will defer processing |
+ // the journal until completion. |
if (need_cleanup) |
- CleanUpBlobJournal(BlobJournalKey::Encode()); |
+ CleanPrimaryJournalIgnoreReturn(); |
db_->Compact(start_key, stop_key); |
return s; |
@@ -2429,16 +2478,11 @@ void IndexedDBBackingStore::ReportBlobUnused(int64 database_id, |
scoped_refptr<LevelDBTransaction> transaction = |
IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get()); |
- std::string live_blob_key = LiveBlobJournalKey::Encode(); |
- BlobJournalType live_blob_journal; |
- if (!GetBlobJournal(live_blob_key, transaction.get(), &live_blob_journal) |
- .ok()) |
+ BlobJournalType live_blob_journal, primary_journal; |
+ if (!GetLiveBlobJournal(transaction.get(), &live_blob_journal).ok()) |
return; |
DCHECK(live_blob_journal.size()); |
- |
- std::string primary_key = BlobJournalKey::Encode(); |
- BlobJournalType primary_journal; |
- if (!GetBlobJournal(primary_key, transaction.get(), &primary_journal).ok()) |
+ if (!GetPrimaryBlobJournal(transaction.get(), &primary_journal).ok()) |
return; |
// There are several cases to handle. If blob_key is kAllBlobsKey, we want to |
@@ -2479,8 +2523,8 @@ void IndexedDBBackingStore::ReportBlobUnused(int64 database_id, |
primary_journal.push_back( |
std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey)); |
} |
- UpdatePrimaryJournalWithBlobList(transaction.get(), primary_journal); |
- UpdateLiveBlobJournalWithBlobList(transaction.get(), new_live_blob_journal); |
+ UpdatePrimaryBlobJournal(transaction.get(), primary_journal); |
+ UpdateLiveBlobJournal(transaction.get(), new_live_blob_journal); |
transaction->Commit(); |
// We could just do the deletions/cleaning here, but if there are a lot of |
// blobs about to be garbage collected, it'd be better to wait and do them all |
@@ -2501,7 +2545,8 @@ void IndexedDBBackingStore::StartJournalCleaningTimer() { |
} |
// This assumes a file path of dbId/second-to-LSB-of-counter/counter. |
-FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) { |
+FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, |
+ int64 key) const { |
return GetBlobFileNameForKey(blob_path_, database_id, key); |
} |
@@ -2621,25 +2666,18 @@ leveldb::Status IndexedDBBackingStore::GetIndexes( |
return s; |
} |
-bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) { |
+bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) const { |
FilePath fileName = GetBlobFileName(database_id, key); |
return base::DeleteFile(fileName, false); |
} |
-bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) { |
+bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) const { |
FilePath dirName = GetBlobDirectoryName(blob_path_, database_id); |
return base::DeleteFile(dirName, true); |
} |
-leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal( |
- const std::string& level_db_key) { |
- scoped_refptr<LevelDBTransaction> journal_transaction = |
- IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get()); |
- BlobJournalType journal; |
- leveldb::Status s = |
- GetBlobJournal(level_db_key, journal_transaction.get(), &journal); |
- if (!s.ok()) |
- return s; |
+leveldb::Status IndexedDBBackingStore::CleanUpBlobJournalEntries( |
+ const BlobJournalType& journal) const { |
if (!journal.size()) |
return leveldb::Status::OK(); |
for (const auto& entry : journal) { |
@@ -2655,6 +2693,25 @@ leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal( |
return IOErrorStatus(); |
} |
} |
+ return leveldb::Status::OK(); |
+} |
+ |
+leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal( |
+ const std::string& level_db_key) const { |
+ DCHECK(!committing_transaction_count_); |
+ leveldb::Status s; |
+ scoped_refptr<LevelDBTransaction> journal_transaction = |
+ IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get()); |
+ BlobJournalType journal; |
+ |
+ s = GetBlobJournal(level_db_key, journal_transaction.get(), &journal); |
+ if (!s.ok()) |
+ return s; |
+ if (!journal.size()) |
+ return leveldb::Status::OK(); |
+ s = CleanUpBlobJournalEntries(journal); |
+ if (!s.ok()) |
+ return s; |
ClearBlobJournal(journal_transaction.get(), level_db_key); |
return journal_transaction->Commit(); |
} |
@@ -2723,7 +2780,11 @@ leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord( |
} |
void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() { |
- CleanUpBlobJournal(BlobJournalKey::Encode()); |
+ // While a transaction is busy it is not safe to clean the journal. |
+ if (committing_transaction_count_ > 0) |
+ StartJournalCleaningTimer(); |
+ else |
+ CleanUpBlobJournal(BlobJournalKey::Encode()); |
} |
WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId( |
@@ -3882,7 +3943,7 @@ IndexedDBBackingStore::OpenIndexCursor( |
IndexedDBBackingStore::Transaction::Transaction( |
IndexedDBBackingStore* backing_store) |
- : backing_store_(backing_store), database_id_(-1) { |
+ : backing_store_(backing_store), database_id_(-1), committing_(false) { |
} |
IndexedDBBackingStore::Transaction::~Transaction() { |
@@ -3890,6 +3951,7 @@ IndexedDBBackingStore::Transaction::~Transaction() { |
blob_change_map_.begin(), blob_change_map_.end()); |
STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(), |
incognito_blob_map_.end()); |
+ DCHECK(!committing_); |
} |
void IndexedDBBackingStore::Transaction::Begin() { |
@@ -3914,59 +3976,60 @@ leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction( |
if (backing_store_->is_incognito()) |
return leveldb::Status::OK(); |
- new_blob_entries->clear(); |
- new_files_to_write->clear(); |
- if (!blob_change_map_.empty()) { |
- // Create LevelDBTransaction for the name generator seed and add-journal. |
- scoped_refptr<LevelDBTransaction> pre_transaction = |
- IndexedDBClassFactory::Get()->CreateLevelDBTransaction( |
- backing_store_->db_.get()); |
- BlobJournalType journal; |
- for (auto& iter : blob_change_map_) { |
- std::vector<IndexedDBBlobInfo*> new_blob_keys; |
- for (auto& entry : iter.second->mutable_blob_info()) { |
- int64 next_blob_key = -1; |
- bool result = GetBlobKeyGeneratorCurrentNumber( |
- pre_transaction.get(), database_id_, &next_blob_key); |
- if (!result || next_blob_key < 0) |
- return InternalInconsistencyStatus(); |
- BlobJournalEntryType journal_entry = |
- std::make_pair(database_id_, next_blob_key); |
- journal.push_back(journal_entry); |
- if (entry.is_file() && !entry.file_path().empty()) { |
- new_files_to_write->push_back( |
- WriteDescriptor(entry.file_path(), |
- next_blob_key, |
- entry.size(), |
- entry.last_modified())); |
- } else { |
- new_files_to_write->push_back( |
- WriteDescriptor(getURLFromUUID(entry.uuid()), |
- next_blob_key, |
- entry.size(), |
- entry.last_modified())); |
- } |
- entry.set_key(next_blob_key); |
- new_blob_keys.push_back(&entry); |
- result = UpdateBlobKeyGeneratorCurrentNumber( |
- pre_transaction.get(), database_id_, next_blob_key + 1); |
- if (!result) |
- return InternalInconsistencyStatus(); |
- } |
- BlobEntryKey blob_entry_key; |
- StringPiece key_piece(iter.second->key()); |
- if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) { |
- NOTREACHED(); |
+ DCHECK(new_blob_entries->empty()); |
+ DCHECK(new_files_to_write->empty()); |
+ DCHECK(blobs_to_write_.empty()); |
+ |
+ if (blob_change_map_.empty()) |
+ return leveldb::Status::OK(); |
+ |
+ // Create LevelDBTransaction for the name generator seed and add-journal. |
+ scoped_refptr<LevelDBTransaction> pre_transaction = |
+ IndexedDBClassFactory::Get()->CreateLevelDBTransaction( |
+ backing_store_->db_.get()); |
+ |
+ for (auto& iter : blob_change_map_) { |
+ std::vector<IndexedDBBlobInfo*> new_blob_keys; |
+ for (auto& entry : iter.second->mutable_blob_info()) { |
+ int64 next_blob_key = -1; |
+ bool result = GetBlobKeyGeneratorCurrentNumber( |
+ pre_transaction.get(), database_id_, &next_blob_key); |
+ if (!result || next_blob_key < 0) |
return InternalInconsistencyStatus(); |
+ BlobJournalEntryType journal_entry = |
+ std::make_pair(database_id_, next_blob_key); |
+ blobs_to_write_.push_back(journal_entry); |
cmumford
2015/01/22 00:56:57
Nit: Do you even need the journal_entry variable?
jsbell
2015/01/22 19:40:45
Nope - just made it inline.
|
+ if (entry.is_file() && !entry.file_path().empty()) { |
+ new_files_to_write->push_back( |
+ WriteDescriptor(entry.file_path(), next_blob_key, entry.size(), |
+ entry.last_modified())); |
+ } else { |
+ new_files_to_write->push_back( |
+ WriteDescriptor(getURLFromUUID(entry.uuid()), next_blob_key, |
+ entry.size(), entry.last_modified())); |
} |
- new_blob_entries->push_back( |
- std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys))); |
+ entry.set_key(next_blob_key); |
+ new_blob_keys.push_back(&entry); |
+ result = UpdateBlobKeyGeneratorCurrentNumber( |
+ pre_transaction.get(), database_id_, next_blob_key + 1); |
+ if (!result) |
+ return InternalInconsistencyStatus(); |
} |
- UpdatePrimaryJournalWithBlobList(pre_transaction.get(), journal); |
- leveldb::Status s = pre_transaction->Commit(); |
- if (!s.ok()) |
+ BlobEntryKey blob_entry_key; |
+ StringPiece key_piece(iter.second->key()); |
+ if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) { |
+ NOTREACHED(); |
return InternalInconsistencyStatus(); |
+ } |
+ new_blob_entries->push_back( |
+ std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys))); |
} |
+ |
+ AppendBlobsToPrimaryBlobJournal(pre_transaction.get(), blobs_to_write_); |
+ leveldb::Status s = pre_transaction->Commit(); |
+ if (!s.ok()) |
+ return InternalInconsistencyStatus(); |
+ |
return leveldb::Status::OK(); |
} |
@@ -4010,24 +4073,17 @@ bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() { |
return true; |
} |
-leveldb::Status IndexedDBBackingStore::Transaction::SortBlobsToRemove() { |
+void IndexedDBBackingStore::Transaction::PartitionBlobsToRemove( |
+ BlobJournalType* dead_blobs, |
+ BlobJournalType* live_blobs) const { |
IndexedDBActiveBlobRegistry* registry = |
backing_store_->active_blob_registry(); |
- BlobJournalType primary_journal, live_blob_journal; |
for (const auto& iter : blobs_to_remove_) { |
if (registry->MarkDeletedCheckIfUsed(iter.first, iter.second)) |
- live_blob_journal.push_back(iter); |
+ live_blobs->push_back(iter); |
else |
- primary_journal.push_back(iter); |
+ dead_blobs->push_back(iter); |
} |
- UpdatePrimaryJournalWithBlobList(transaction_.get(), primary_journal); |
- leveldb::Status s = |
- MergeBlobsIntoLiveBlobJournal(transaction_.get(), live_blob_journal); |
- if (!s.ok()) |
- return s; |
- // To signal how many blobs need attention right now. |
- blobs_to_remove_.swap(primary_journal); |
- return leveldb::Status::OK(); |
} |
leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne( |
@@ -4038,13 +4094,6 @@ leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne( |
leveldb::Status s; |
- s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode()); |
- if (!s.ok()) { |
- INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD); |
- transaction_ = NULL; |
- return s; |
- } |
- |
BlobEntryKeyValuePairVec new_blob_entries; |
WriteDescriptorVec new_files_to_write; |
s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write); |
@@ -4062,13 +4111,13 @@ leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne( |
return InternalInconsistencyStatus(); |
} |
+ committing_ = true; |
+ ++backing_store_->committing_transaction_count_; |
+ |
if (new_files_to_write.size()) { |
// This kicks off the writes of the new blobs, if any. |
// This call will zero out new_blob_entries and new_files_to_write. |
WriteNewBlobs(&new_blob_entries, &new_files_to_write, callback); |
- // Remove the add journal, if any; once the blobs are written, and we |
- // commit, this will do the cleanup. |
- ClearBlobJournal(transaction_.get(), BlobJournalKey::Encode()); |
} else { |
callback->Run(true); |
} |
@@ -4079,37 +4128,95 @@ leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne( |
leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() { |
IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo"); |
leveldb::Status s; |
- if (blobs_to_remove_.size()) { |
- s = SortBlobsToRemove(); |
- if (!s.ok()) { |
- INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD); |
- transaction_ = NULL; |
+ |
+ DCHECK(committing_); |
+ committing_ = false; |
+ --backing_store_->committing_transaction_count_; |
cmumford
2015/01/22 00:56:57
We sure it won't underflow? DCHECK maybe?
jsbell
2015/01/22 19:40:45
Added a DCHECK (here and in Rollback). I wasn't su
|
+ |
+ // Read the persisted states of the primary/live blob journals, |
+ // so that they can be updated correctly by the transaction. |
+ BlobJournalType primary_journal, live_journal; |
+ { |
+ scoped_refptr<LevelDBTransaction> journal_transaction = |
+ IndexedDBClassFactory::Get()->CreateLevelDBTransaction( |
+ backing_store_->db_.get()); |
+ s = GetPrimaryBlobJournal(journal_transaction.get(), &primary_journal); |
+ if (!s.ok()) |
+ return s; |
+ s = GetLiveBlobJournal(journal_transaction.get(), &live_journal); |
+ if (!s.ok()) |
return s; |
- } |
} |
+ // Remove newly added blobs from the journal - they will be accounted |
+ // for in blob entry tables in the transaction. |
+ { |
+ std::sort(primary_journal.begin(), primary_journal.end()); |
+ std::sort(blobs_to_write_.begin(), blobs_to_write_.end()); |
+ BlobJournalType new_journal = base::STLSetDifference<BlobJournalType>( |
+ primary_journal, blobs_to_write_); |
+ primary_journal.swap(new_journal); |
+ } |
+ |
+ // Append newly deleted blobs to appropriate primary/live journals. |
+ BlobJournalType saved_primary_journal = primary_journal; |
+ BlobJournalType dead_blobs, live_blobs; |
+ if (blobs_to_remove_.size()) { |
+ DCHECK(!backing_store_->is_incognito()); |
+ PartitionBlobsToRemove(&dead_blobs, &live_blobs); |
+ } |
+ primary_journal.insert(primary_journal.end(), dead_blobs.begin(), |
+ dead_blobs.end()); |
+ live_journal.insert(live_journal.end(), live_blobs.begin(), live_blobs.end()); |
+ UpdatePrimaryBlobJournal(transaction_.get(), primary_journal); |
+ UpdateLiveBlobJournal(transaction_.get(), live_journal); |
+ |
+ // Actually commit. If this succeeds, the journals will appropriately |
+ // reflect pending blob work - dead files that should be deleted |
+ // immediately, and live files to monitor. |
s = transaction_->Commit(); |
transaction_ = NULL; |
- if (s.ok() && backing_store_->is_incognito() && !blob_change_map_.empty()) { |
- BlobChangeMap& target_map = backing_store_->incognito_blob_map_; |
- for (auto& iter : blob_change_map_) { |
- BlobChangeMap::iterator target_record = target_map.find(iter.first); |
- if (target_record != target_map.end()) { |
- delete target_record->second; |
- target_map.erase(target_record); |
- } |
- if (iter.second) { |
- target_map[iter.first] = iter.second; |
- iter.second = NULL; |
+ if (!s.ok()) { |
+ INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD); |
+ return s; |
+ } |
+ |
+ if (backing_store_->is_incognito()) { |
+ if (!blob_change_map_.empty()) { |
+ BlobChangeMap& target_map = backing_store_->incognito_blob_map_; |
+ for (auto& iter : blob_change_map_) { |
+ BlobChangeMap::iterator target_record = target_map.find(iter.first); |
+ if (target_record != target_map.end()) { |
+ delete target_record->second; |
+ target_map.erase(target_record); |
+ } |
+ if (iter.second) { |
+ target_map[iter.first] = iter.second; |
+ iter.second = NULL; |
+ } |
} |
} |
+ return leveldb::Status::OK(); |
} |
- if (!s.ok()) |
- INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD); |
- else if (blobs_to_remove_.size()) |
- s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode()); |
+ // Actually delete dead blob files, then remove those entries |
+ // from the persisted primary journal. |
+ if (!dead_blobs.size()) |
+ return leveldb::Status::OK(); |
+ |
+ s = backing_store_->CleanUpBlobJournalEntries(dead_blobs); |
+ if (!s.ok()) { |
+ INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD); |
+ return s; |
+ } |
+ |
+ scoped_refptr<LevelDBTransaction> update_journal_transaction = |
+ IndexedDBClassFactory::Get()->CreateLevelDBTransaction( |
+ backing_store_->db_.get()); |
+ UpdatePrimaryBlobJournal(update_journal_transaction.get(), |
+ saved_primary_journal); |
+ s = update_journal_transaction->Commit(); |
return s; |
} |
@@ -4161,6 +4268,11 @@ void IndexedDBBackingStore::Transaction::WriteNewBlobs( |
void IndexedDBBackingStore::Transaction::Rollback() { |
IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback"); |
+ if (committing_) { |
+ committing_ = false; |
+ --backing_store_->committing_transaction_count_; |
+ } |
+ |
if (chained_blob_writer_.get()) { |
chained_blob_writer_->Abort(); |
chained_blob_writer_ = NULL; |