Bug 858680 - Part 1: Perform incremental_vacuum on open databases while idle, r=janv.

This commit is contained in:
Ben Turner 2015-04-30 13:47:03 -07:00
parent e92b154f6b
commit 230116ae8b

View File

@ -202,11 +202,11 @@ static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount,
"Idle thread limit must be less than total thread limit!");
// The length of time that database connections will be held open after all
// transactions have completed.
const uint32_t kConnectionIdleCheckpointsMS = 2 * 1000; // 2 seconds
// transactions have completed before doing idle maintenance.
const uint32_t kConnectionIdleMaintenanceMS = 2 * 1000; // 2 seconds
// The length of time that database connections will be held open after all
// transactions and checkpointing have completed.
// transactions and maintenance have completed.
const uint32_t kConnectionIdleCloseMS = 10 * 1000; // 10 seconds
// The length of time that idle threads will stay alive before being shut down.
@ -4339,6 +4339,13 @@ class DatabaseConnection final
{
friend class ConnectionPool;
enum CheckpointMode
{
CheckpointMode_Full,
CheckpointMode_Restart,
CheckpointMode_Truncate
};
public:
class AutoSavepoint;
class CachedStatement;
@ -4350,6 +4357,7 @@ private:
nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
mCachedStatements;
nsRefPtr<UpdateRefcountFunction> mUpdateRefcountFunction;
bool mInReadTransaction;
bool mInWriteTransaction;
#ifdef DEBUG
@ -4393,6 +4401,9 @@ public:
nsresult
BeginWriteTransaction();
nsresult
CommitWriteTransaction();
void
RollbackWriteTransaction();
@ -4409,7 +4420,15 @@ public:
RollbackSavepoint();
nsresult
Checkpoint(bool aIdle);
Checkpoint()
{
AssertIsOnConnectionThread();
return CheckpointInternal(CheckpointMode_Full);
}
void
DoIdleProcessing(bool aNeedsCheckpoint);
void
Close();
@ -4422,6 +4441,19 @@ private:
nsresult
Init();
nsresult
CheckpointInternal(CheckpointMode aMode);
nsresult
GetFreelistCount(CachedStatement& aCachedStatement, uint32_t* aFreelistCount);
nsresult
ReclaimFreePagesWhileIdle(CachedStatement& aFreelistStatement,
CachedStatement& aRollbackStatement,
uint32_t aFreelistCount,
bool aNeedsCheckpoint,
bool* aFreedSomePages);
};
class MOZ_STACK_CLASS DatabaseConnection::AutoSavepoint final
@ -4626,11 +4658,11 @@ public:
private:
class ConnectionRunnable;
class CheckpointConnectionRunnable;
class CloseConnectionRunnable;
struct DatabaseInfo;
struct DatabasesCompleteCallback;
class FinishCallbackWrapper;
class IdleConnectionRunnable;
struct IdleDatabaseInfo;
struct IdleResource;
struct IdleThreadInfo;
@ -4644,6 +4676,7 @@ private:
nsTArray<IdleThreadInfo> mIdleThreads;
nsTArray<IdleDatabaseInfo> mIdleDatabases;
nsTArray<DatabaseInfo*> mDatabasesPerformingIdleMaintenance;
nsCOMPtr<nsITimer> mIdleTimer;
TimeStamp mTargetIdleTime;
@ -4754,7 +4787,7 @@ private:
MaybeFireCallback(DatabasesCompleteCallback* aCallback);
void
CheckpointDatabase(DatabaseInfo* aDatabaseInfo);
PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo);
void
CloseDatabase(DatabaseInfo* aDatabaseInfo);
@ -4778,19 +4811,21 @@ protected:
{ }
};
class ConnectionPool::CheckpointConnectionRunnable final
class ConnectionPool::IdleConnectionRunnable final
: public ConnectionRunnable
{
bool mNeedsCheckpoint;
public:
explicit
CheckpointConnectionRunnable(DatabaseInfo* aDatabaseInfo)
IdleConnectionRunnable(DatabaseInfo* aDatabaseInfo, bool aNeedsCheckpoint)
: ConnectionRunnable(aDatabaseInfo)
, mNeedsCheckpoint(aNeedsCheckpoint)
{ }
NS_DECL_ISUPPORTS_INHERITED
private:
~CheckpointConnectionRunnable()
~IdleConnectionRunnable()
{ }
NS_DECL_NSIRUNNABLE
@ -4842,6 +4877,7 @@ struct ConnectionPool::DatabaseInfo final
uint32_t mReadTransactionCount;
uint32_t mWriteTransactionCount;
bool mNeedsCheckpoint;
bool mIdle;
bool mCloseOnIdle;
bool mClosing;
@ -8405,6 +8441,7 @@ DatabaseConnection::DatabaseConnection(
FileManager* aFileManager)
: mStorageConnection(aStorageConnection)
, mFileManager(aFileManager)
, mInReadTransaction(false)
, mInWriteTransaction(false)
#ifdef DEBUG
, mDEBUGSavepointCount(0)
@ -8430,10 +8467,11 @@ nsresult
DatabaseConnection::Init()
{
AssertIsOnConnectionThread();
MOZ_ASSERT(!mInReadTransaction);
MOZ_ASSERT(!mInWriteTransaction);
CachedStatement stmt;
nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN"), &stmt);
nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &stmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -8443,6 +8481,8 @@ DatabaseConnection::Init()
return rv;
}
mInReadTransaction = true;
return NS_OK;
}
@ -8492,6 +8532,7 @@ DatabaseConnection::BeginWriteTransaction()
{
AssertIsOnConnectionThread();
MOZ_ASSERT(mStorageConnection);
MOZ_ASSERT(mInReadTransaction);
MOZ_ASSERT(!mInWriteTransaction);
PROFILER_LABEL("IndexedDB",
@ -8500,7 +8541,8 @@ DatabaseConnection::BeginWriteTransaction()
// Release our read locks.
CachedStatement rollbackStmt;
nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK"), &rollbackStmt);
nsresult rv =
GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &rollbackStmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -8510,6 +8552,8 @@ DatabaseConnection::BeginWriteTransaction()
return rv;
}
mInReadTransaction = false;
if (!mUpdateRefcountFunction) {
MOZ_ASSERT(mFileManager);
@ -8528,7 +8572,7 @@ DatabaseConnection::BeginWriteTransaction()
}
CachedStatement beginStmt;
rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"), &beginStmt);
rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"), &beginStmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -8562,10 +8606,38 @@ DatabaseConnection::BeginWriteTransaction()
return NS_OK;
}
nsresult
DatabaseConnection::CommitWriteTransaction()
{
AssertIsOnConnectionThread();
MOZ_ASSERT(mStorageConnection);
MOZ_ASSERT(!mInReadTransaction);
MOZ_ASSERT(mInWriteTransaction);
PROFILER_LABEL("IndexedDB",
"DatabaseConnection::CommitWriteTransaction",
js::ProfileEntry::Category::STORAGE);
CachedStatement stmt;
nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &stmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = stmt->Execute();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mInWriteTransaction = false;
return NS_OK;
}
void
DatabaseConnection::RollbackWriteTransaction()
{
AssertIsOnConnectionThread();
MOZ_ASSERT(!mInReadTransaction);
MOZ_ASSERT(mStorageConnection);
PROFILER_LABEL("IndexedDB",
@ -8577,7 +8649,7 @@ DatabaseConnection::RollbackWriteTransaction()
}
DatabaseConnection::CachedStatement stmt;
nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK"), &stmt);
nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &stmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
@ -8585,6 +8657,8 @@ DatabaseConnection::RollbackWriteTransaction()
// This may fail if SQLite already rolled back the transaction so ignore any
// errors.
unused << stmt->Execute();
mInWriteTransaction = false;
}
void
@ -8592,6 +8666,8 @@ DatabaseConnection::FinishWriteTransaction()
{
AssertIsOnConnectionThread();
MOZ_ASSERT(mStorageConnection);
MOZ_ASSERT(!mInReadTransaction);
MOZ_ASSERT(!mInWriteTransaction);
PROFILER_LABEL("IndexedDB",
"DatabaseConnection::FinishWriteTransaction",
@ -8601,14 +8677,8 @@ DatabaseConnection::FinishWriteTransaction()
mUpdateRefcountFunction->Reset();
}
if (!mInWriteTransaction) {
return;
}
mInWriteTransaction = false;
CachedStatement stmt;
nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN"), &stmt);
nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &stmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
@ -8617,6 +8687,8 @@ DatabaseConnection::FinishWriteTransaction()
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
mInReadTransaction = true;
}
nsresult
@ -8718,23 +8790,46 @@ DatabaseConnection::RollbackSavepoint()
}
nsresult
DatabaseConnection::Checkpoint(bool aIdle)
DatabaseConnection::CheckpointInternal(CheckpointMode aMode)
{
AssertIsOnConnectionThread();
MOZ_ASSERT(!mInReadTransaction);
MOZ_ASSERT(!mInWriteTransaction);
PROFILER_LABEL("IndexedDB",
"DatabaseConnection::Checkpoint",
"DatabaseConnection::CheckpointInternal",
js::ProfileEntry::Category::STORAGE);
nsAutoCString stmtString;
stmtString.AssignLiteral("PRAGMA wal_checkpoint(");
switch (aMode) {
case CheckpointMode_Full:
// Ensures that the database is completely checkpointed and flushed to
// disk.
stmtString.AppendLiteral("FULL");
break;
case CheckpointMode_Restart:
// Like CheckpointMode_Full, but also ensures that the next write will
// start overwriting the existing WAL file rather than letting the WAL
// file grow.
stmtString.AppendLiteral("RESTART");
break;
case CheckpointMode_Truncate:
// Like CheckpointMode_Restart but also truncates the existing WAL file.
stmtString.AppendLiteral("TRUNCATE");
break;
default:
MOZ_CRASH("Unknown CheckpointMode!");
}
stmtString.AppendLiteral(");");
CachedStatement stmt;
nsresult rv =
GetCachedStatement(aIdle ?
// When idle we want to reclaim disk space.
NS_LITERAL_CSTRING("PRAGMA wal_checkpoint(TRUNCATE)") :
// We're being called at the end of a READ_WRITE_FLUSH transaction so make
// sure that the database is completely checkpointed and flushed to disk.
NS_LITERAL_CSTRING("PRAGMA wal_checkpoint(FULL)"),
&stmt);
nsresult rv = GetCachedStatement(stmtString, &stmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -8747,6 +8842,246 @@ DatabaseConnection::Checkpoint(bool aIdle)
return NS_OK;
}
void
DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint)
{
AssertIsOnConnectionThread();
MOZ_ASSERT(mInReadTransaction);
MOZ_ASSERT(!mInWriteTransaction);
PROFILER_LABEL("IndexedDB",
"DatabaseConnection::DoIdleProcessing",
js::ProfileEntry::Category::STORAGE);
DatabaseConnection::CachedStatement freelistStmt;
uint32_t freelistCount;
nsresult rv = GetFreelistCount(freelistStmt, &freelistCount);
if (NS_WARN_IF(NS_FAILED(rv))) {
freelistCount = 0;
}
CachedStatement rollbackStmt;
CachedStatement beginStmt;
if (aNeedsCheckpoint || freelistCount) {
rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &rollbackStmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &beginStmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
// Release the connection's normal transaction. It's possible that it could
// fail, but that isn't a problem here.
unused << rollbackStmt->Execute();
mInReadTransaction = false;
}
bool freedSomePages = false;
if (freelistCount) {
rv = ReclaimFreePagesWhileIdle(freelistStmt,
rollbackStmt,
freelistCount,
aNeedsCheckpoint,
&freedSomePages);
if (NS_WARN_IF(NS_FAILED(rv))) {
MOZ_ASSERT(!freedSomePages);
}
// Make sure we didn't leave a transaction running.
MOZ_ASSERT(!mInReadTransaction);
MOZ_ASSERT(!mInWriteTransaction);
}
// Truncate the WAL if we were asked to or if we managed to free some space.
if (aNeedsCheckpoint || freedSomePages) {
rv = CheckpointInternal(CheckpointMode_Truncate);
unused << NS_WARN_IF(NS_FAILED(rv));
}
// Finally try to restart the read transaction if we rolled it back earlier.
if (beginStmt) {
rv = beginStmt->Execute();
if (NS_SUCCEEDED(rv)) {
mInReadTransaction = true;
} else {
NS_WARNING("Falied to restart read transaction!");
}
}
}
nsresult
DatabaseConnection::ReclaimFreePagesWhileIdle(
CachedStatement& aFreelistStatement,
CachedStatement& aRollbackStatement,
uint32_t aFreelistCount,
bool aNeedsCheckpoint,
bool* aFreedSomePages)
{
AssertIsOnConnectionThread();
MOZ_ASSERT(aFreelistStatement);
MOZ_ASSERT(aRollbackStatement);
MOZ_ASSERT(aFreelistCount);
MOZ_ASSERT(aFreedSomePages);
MOZ_ASSERT(!mInReadTransaction);
MOZ_ASSERT(!mInWriteTransaction);
PROFILER_LABEL("IndexedDB",
"DatabaseConnection::ReclaimFreePagesWhileIdle",
js::ProfileEntry::Category::STORAGE);
// Make sure we don't keep working if anything else needs this thread.
nsIThread* currentThread = NS_GetCurrentThread();
MOZ_ASSERT(currentThread);
if (NS_HasPendingEvents(currentThread)) {
*aFreedSomePages = false;
return NS_OK;
}
// Only try to free 10% at a time so that we can bail out if this connection
// suddenly becomes active or if the thread is needed otherwise.
nsAutoCString stmtString;
stmtString.AssignLiteral("PRAGMA incremental_vacuum(");
stmtString.AppendInt(std::max(uint64_t(1), uint64_t(aFreelistCount / 10)));
stmtString.AppendLiteral(");");
// Make all the statements we'll need up front.
CachedStatement incrementalVacuumStmt;
nsresult rv = GetCachedStatement(stmtString, &incrementalVacuumStmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
CachedStatement beginImmediateStmt;
rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"),
&beginImmediateStmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
CachedStatement commitStmt;
rv = GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &commitStmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (aNeedsCheckpoint) {
// Freeing pages is a journaled operation, so it will require additional WAL
// space. However, we're idle and are about to checkpoint anyway, so doing a
// RESTART checkpoint here should allow us to reuse any existing space.
rv = CheckpointInternal(CheckpointMode_Restart);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// Start the write transaction.
rv = beginImmediateStmt->Execute();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mInWriteTransaction = true;
bool freedSomePages = false;
while (aFreelistCount) {
if (NS_HasPendingEvents(currentThread)) {
// Something else wants to use the thread so roll back this transaction.
// It's ok if we never make progress here because the idle service should
// eventually reclaim this space.
rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
break;
}
rv = incrementalVacuumStmt->Execute();
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
freedSomePages = true;
rv = GetFreelistCount(aFreelistStatement, &aFreelistCount);
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
}
if (NS_SUCCEEDED(rv) && freedSomePages) {
// Commit the write transaction.
rv = commitStmt->Execute();
if (NS_SUCCEEDED(rv)) {
mInWriteTransaction = false;
} else {
NS_WARNING("Failed to commit!");
}
}
if (NS_FAILED(rv)) {
MOZ_ASSERT(mInWriteTransaction);
// Something failed, make sure we roll everything back.
unused << aRollbackStatement->Execute();
mInWriteTransaction = false;
return rv;
}
*aFreedSomePages = freedSomePages;
return NS_OK;
}
nsresult
DatabaseConnection::GetFreelistCount(CachedStatement& aCachedStatement,
uint32_t* aFreelistCount)
{
AssertIsOnConnectionThread();
MOZ_ASSERT(aFreelistCount);
PROFILER_LABEL("IndexedDB",
"DatabaseConnection::GetFreelistCount",
js::ProfileEntry::Category::STORAGE);
nsresult rv;
if (!aCachedStatement) {
rv = GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA freelist_count;"),
&aCachedStatement);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
bool hasResult;
rv = aCachedStatement->ExecuteStep(&hasResult);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(hasResult);
// Make sure this statement is reset when leaving this function since we're
// not using the normal stack-based protection of CachedStatement.
mozStorageStatementScoper scoper(aCachedStatement);
int32_t freelistCount;
rv = aCachedStatement->GetInt32(0, &freelistCount);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(freelistCount >= 0);
*aFreelistCount = uint32_t(freelistCount);
return NS_OK;
}
void
DatabaseConnection::Close()
{
@ -9497,8 +9832,8 @@ ConnectionPool::IdleTimerCallback(nsITimer* aTimer, void* aClosure)
IdleDatabaseInfo& info = self->mIdleDatabases[index];
if (now >= info.mIdleTime) {
if (info.mDatabaseInfo->mNeedsCheckpoint) {
self->CheckpointDatabase(info.mDatabaseInfo);
if (info.mDatabaseInfo->mIdle) {
self->PerformIdleDatabaseMaintenance(info.mDatabaseInfo);
} else {
self->CloseDatabase(info.mDatabaseInfo);
}
@ -9977,13 +10312,19 @@ ConnectionPool::CloseIdleDatabases()
js::ProfileEntry::Category::STORAGE);
if (!mIdleDatabases.IsEmpty()) {
for (uint32_t count = mIdleDatabases.Length(), index = 0;
index < count;
index++) {
CloseDatabase(mIdleDatabases[index].mDatabaseInfo);
for (IdleDatabaseInfo& idleInfo : mIdleDatabases) {
CloseDatabase(idleInfo.mDatabaseInfo);
}
mIdleDatabases.Clear();
}
if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) {
for (DatabaseInfo* dbInfo : mDatabasesPerformingIdleMaintenance) {
MOZ_ASSERT(dbInfo);
CloseDatabase(dbInfo);
}
mDatabasesPerformingIdleMaintenance.Clear();
}
}
void
@ -10020,6 +10361,8 @@ ConnectionPool::ScheduleTransaction(TransactionInfo* aTransactionInfo,
DatabaseInfo* dbInfo = aTransactionInfo->mDatabaseInfo;
MOZ_ASSERT(dbInfo);
dbInfo->mIdle = false;
if (dbInfo->mClosing) {
MOZ_ASSERT(!mIdleDatabases.Contains(dbInfo));
MOZ_ASSERT(
@ -10054,6 +10397,23 @@ ConnectionPool::ScheduleTransaction(TransactionInfo* aTransactionInfo,
} else {
NS_WARNING("Failed to make new thread!");
}
} else if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) {
// We need a thread right now so force all idle processing to stop by
// posting a dummy runnable to each thread that might be doing idle
// maintenance.
nsCOMPtr<nsIRunnable> runnable = new nsRunnable();
for (uint32_t index = mDatabasesPerformingIdleMaintenance.Length();
index > 0;
index--) {
DatabaseInfo* dbInfo = mDatabasesPerformingIdleMaintenance[index - 1];
MOZ_ASSERT(dbInfo);
MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
dbInfo->mThreadInfo.mThread->Dispatch(runnable,
NS_DISPATCH_NORMAL)));
}
}
if (!created) {
@ -10221,6 +10581,9 @@ ConnectionPool::NoteFinishedTransaction(uint64_t aTransactionId)
#endif
if (!dbInfo->TotalTransactionCount()) {
MOZ_ASSERT(!dbInfo->mIdle);
dbInfo->mIdle = true;
NoteIdleDatabase(dbInfo);
}
}
@ -10433,21 +10796,26 @@ ConnectionPool::MaybeFireCallback(DatabasesCompleteCallback* aCallback)
}
void
ConnectionPool::CheckpointDatabase(DatabaseInfo* aDatabaseInfo)
ConnectionPool::PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo)
{
AssertIsOnOwningThread();
MOZ_ASSERT(aDatabaseInfo);
MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
MOZ_ASSERT(aDatabaseInfo->mNeedsCheckpoint);
MOZ_ASSERT(aDatabaseInfo->mIdle);
MOZ_ASSERT(!aDatabaseInfo->mCloseOnIdle);
MOZ_ASSERT(!aDatabaseInfo->mClosing);
aDatabaseInfo->mNeedsCheckpoint = false;
MOZ_ASSERT(mIdleDatabases.Contains(aDatabaseInfo));
MOZ_ASSERT(!mDatabasesPerformingIdleMaintenance.Contains(aDatabaseInfo));
nsCOMPtr<nsIRunnable> runnable =
new CheckpointConnectionRunnable(aDatabaseInfo);
new IdleConnectionRunnable(aDatabaseInfo, aDatabaseInfo->mNeedsCheckpoint);
aDatabaseInfo->mNeedsCheckpoint = false;
aDatabaseInfo->mIdle = false;
mDatabasesPerformingIdleMaintenance.AppendElement(aDatabaseInfo);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable,
@ -10464,6 +10832,7 @@ ConnectionPool::CloseDatabase(DatabaseInfo* aDatabaseInfo)
MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
MOZ_ASSERT(!aDatabaseInfo->mClosing);
aDatabaseInfo->mIdle = false;
aDatabaseInfo->mNeedsCheckpoint = false;
aDatabaseInfo->mClosing = true;
@ -10485,7 +10854,8 @@ ConnectionPool::CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId)
js::ProfileEntry::Category::STORAGE);
if (DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId)) {
if (mIdleDatabases.RemoveElement(dbInfo)) {
if (mIdleDatabases.RemoveElement(dbInfo) ||
mDatabasesPerformingIdleMaintenance.RemoveElement(dbInfo)) {
CloseDatabase(dbInfo);
AdjustIdleTimer();
} else {
@ -10510,38 +10880,43 @@ ConnectionRunnable::ConnectionRunnable(DatabaseInfo* aDatabaseInfo)
MOZ_ASSERT(mOwningThread);
}
NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::CheckpointConnectionRunnable,
NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::IdleConnectionRunnable,
ConnectionPool::ConnectionRunnable)
NS_IMETHODIMP
ConnectionPool::
CheckpointConnectionRunnable::Run()
IdleConnectionRunnable::Run()
{
MOZ_ASSERT(mDatabaseInfo);
MOZ_ASSERT(!mDatabaseInfo->mIdle);
PROFILER_LABEL("IndexedDB",
"ConnectionPool::CheckpointConnectionRunnable::Run",
js::ProfileEntry::Category::STORAGE);
nsCOMPtr<nsIEventTarget> owningThread;
mOwningThread.swap(owningThread);
if (mOwningThread) {
if (owningThread) {
mDatabaseInfo->AssertIsOnConnectionThread();
MOZ_ASSERT(mDatabaseInfo->mConnection);
nsCOMPtr<nsIEventTarget> owningThread;
mOwningThread.swap(owningThread);
mDatabaseInfo->mConnection->Checkpoint(/* aIdle */ true);
mDatabaseInfo->mConnection->DoIdleProcessing(mNeedsCheckpoint);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
owningThread->Dispatch(this, NS_DISPATCH_NORMAL)));
return NS_OK;
}
if (!mDatabaseInfo->TotalTransactionCount()) {
nsRefPtr<ConnectionPool> connectionPool = mDatabaseInfo->mConnectionPool;
MOZ_ASSERT(connectionPool);
nsRefPtr<ConnectionPool> connectionPool = mDatabaseInfo->mConnectionPool;
MOZ_ASSERT(connectionPool);
connectionPool->NoteIdleDatabase(mDatabaseInfo);
if (mDatabaseInfo->mClosing) {
MOZ_ASSERT(!connectionPool->
mDatabasesPerformingIdleMaintenance.Contains(mDatabaseInfo));
} else {
MOZ_ALWAYS_TRUE(
connectionPool->
mDatabasesPerformingIdleMaintenance.RemoveElement(mDatabaseInfo));
if (!mDatabaseInfo->TotalTransactionCount()) {
connectionPool->NoteIdleDatabase(mDatabaseInfo);
}
}
return NS_OK;
@ -10602,6 +10977,7 @@ DatabaseInfo::DatabaseInfo(ConnectionPool* aConnectionPool,
, mReadTransactionCount(0)
, mWriteTransactionCount(0)
, mNeedsCheckpoint(false)
, mIdle(false)
, mCloseOnIdle(false)
, mClosing(false)
#ifdef DEBUG
@ -10874,9 +11250,9 @@ IdleResource::~IdleResource()
ConnectionPool::
IdleDatabaseInfo::IdleDatabaseInfo(DatabaseInfo* aDatabaseInfo)
: IdleResource(TimeStamp::NowLoRes() +
(aDatabaseInfo->mNeedsCheckpoint ?
TimeDuration::FromMilliseconds(kConnectionIdleCheckpointsMS) :
TimeDuration::FromMilliseconds(kConnectionIdleCloseMS)))
(aDatabaseInfo->mIdle ?
TimeDuration::FromMilliseconds(kConnectionIdleMaintenanceMS) :
TimeDuration::FromMilliseconds(kConnectionIdleCloseMS)))
, mDatabaseInfo(aDatabaseInfo)
{
AssertIsOnBackgroundThread();
@ -16280,12 +16656,13 @@ DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes(
keyRangeClause);
}
rv = aConnection->GetCachedStatement(
NS_LITERAL_CSTRING("SELECT index_data_values, key "
"FROM object_data "
"WHERE object_store_id = :") + objectStoreIdString +
keyRangeClause +
NS_LITERAL_CSTRING(";"),
rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
"SELECT index_data_values, key "
"FROM object_data "
"WHERE object_store_id = :") +
objectStoreIdString +
keyRangeClause +
NS_LITERAL_CSTRING(";"),
&selectStmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -18411,9 +18788,9 @@ VersionChangeOp::DoDatabaseWork(DatabaseConnection* aConnection)
}
DatabaseConnection::CachedStatement updateStmt;
rv = aConnection->GetCachedStatement(
NS_LITERAL_CSTRING("UPDATE database "
"SET version = :version"),
rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
"UPDATE database "
"SET version = :version;"),
&updateStmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -19462,7 +19839,8 @@ CommitOp::AssertForeignKeyConsistency(DatabaseConnection* aConnection)
DatabaseConnection::CachedStatement pragmaStmt;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
aConnection->GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA foreign_keys;"), &pragmaStmt)));
aConnection->GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA foreign_keys;"),
&pragmaStmt)));
bool hasResult;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(pragmaStmt->ExecuteStep(&hasResult)));
@ -19476,7 +19854,9 @@ CommitOp::AssertForeignKeyConsistency(DatabaseConnection* aConnection)
DatabaseConnection::CachedStatement checkStmt;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
aConnection->GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"), &checkStmt)));
aConnection->GetCachedStatement(
NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"),
&checkStmt)));
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(checkStmt->ExecuteStep(&hasResult)));
@ -19528,23 +19908,16 @@ CommitOp::Run()
if (NS_SUCCEEDED(mResultCode)) {
AssertForeignKeyConsistency(connection);
DatabaseConnection::CachedStatement stmt;
mResultCode = connection->GetCachedStatement(NS_LITERAL_CSTRING("COMMIT"), &stmt);
NS_WARN_IF_FALSE(NS_SUCCEEDED(mResultCode),
"Failed to get 'COMMIT' statement!");
mResultCode = connection->CommitWriteTransaction();
NS_WARN_IF_FALSE(NS_SUCCEEDED(mResultCode), "Commit failed!");
if (NS_SUCCEEDED(mResultCode)) {
mResultCode = stmt->Execute();
NS_WARN_IF_FALSE(NS_SUCCEEDED(mResultCode), "Commit failed!");
if (NS_SUCCEEDED(mResultCode) &&
mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH) {
mResultCode = connection->Checkpoint();
}
if (mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH &&
NS_SUCCEEDED(mResultCode)) {
mResultCode = connection->Checkpoint(/* aIdle */ false);
}
if (NS_SUCCEEDED(mResultCode) && fileRefcountFunction) {
fileRefcountFunction->DidCommit();
}
if (NS_SUCCEEDED(mResultCode) && fileRefcountFunction) {
fileRefcountFunction->DidCommit();
}
}
}
@ -19704,7 +20077,7 @@ CreateObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection)
DatabaseConnection::CachedStatement stmt;
rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING(
"INSERT INTO object_store (id, auto_increment, name, key_path) "
"VALUES (:id, :auto_increment, :name, :key_path)"),
"VALUES (:id, :auto_increment, :name, :key_path);"),
&stmt);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;