Bug 1900093 - Add OPEN_NOT_EXCLUSIVE option for opening sqlite database. r=mak

Differential Revision: https://phabricator.services.mozilla.com/D212466
This commit is contained in:
Niklas Baumgardner
2024-06-12 00:17:58 +00:00
parent 756bca1e70
commit bdf028d4e2
5 changed files with 36 additions and 10 deletions

View File

@@ -75,6 +75,15 @@ interface mozIStorageService : nsISupports {
*/
const unsigned long OPEN_IGNORE_LOCKING_MODE = 1 << 2;
/**
* Allow multi-process access to the database file.
* Normally this option is disabled as exclusive locking performs better
* and provides some protection against third party manipulation of hot
* databases. Note however this option only shows its effects on Unix
* systems because exclusive locking is not yet implemented on Windows.
*/
const unsigned long OPEN_NOT_EXCLUSIVE = 1 << 3;
/**
* All optional connection object features are off.
*/
@@ -121,8 +130,7 @@ interface mozIStorageService : nsISupports {
*
* @param aOpenFlags
* A set of flags to open the database with optional features.
* Currently supports OPEN_SHARED, OPEN_READONLY and
* OPEN_IGNORE_LOCKING_MODE flags.
* See OPEN_* options above.
* For full details, please refer to the documentation of the flags.
*
* @param aConnectionFlags

View File

@@ -785,7 +785,7 @@ NS_IMPL_ISUPPORTS_INHERITED(AsyncBackupDatabaseFile, Runnable, nsITimerCallback)
Connection::Connection(Service* aService, int aFlags,
ConnectionOperation aSupportedOperations,
const nsCString& aTelemetryFilename, bool aInterruptible,
bool aIgnoreLockingMode)
bool aIgnoreLockingMode, bool aOpenNotExclusive)
: sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex"),
sharedDBMutex("Connection::sharedDBMutex"),
eventTargetOpenedOn(WrapNotNull(GetCurrentSerialEventTarget())),
@@ -801,6 +801,7 @@ Connection::Connection(Service* aService, int aFlags,
mInterruptible(aSupportedOperations == Connection::ASYNCHRONOUS ||
aInterruptible),
mIgnoreLockingMode(aIgnoreLockingMode),
mOpenNotExclusive(aOpenNotExclusive),
mAsyncExecutionThreadShuttingDown(false),
mConnectionClosed(false),
mGrowthChunkSize(0) {
@@ -1086,7 +1087,8 @@ nsresult Connection::initialize(nsIFile* aDatabaseFile) {
nsresult rv = aDatabaseFile->GetPath(path);
NS_ENSURE_SUCCESS(rv, rv);
bool exclusive = StaticPrefs::storage_sqlite_exclusiveLock_enabled();
bool exclusive =
StaticPrefs::storage_sqlite_exclusiveLock_enabled() && !mOpenNotExclusive;
int srv;
if (mIgnoreLockingMode) {
exclusive = false;
@@ -1170,7 +1172,8 @@ nsresult Connection::initialize(nsIFileURL* aFileURL) {
return true;
}));
bool exclusive = StaticPrefs::storage_sqlite_exclusiveLock_enabled();
bool exclusive =
StaticPrefs::storage_sqlite_exclusiveLock_enabled() && !mOpenNotExclusive;
const char* const vfs = hasKey ? obfsvfs::GetVFSName()
: hasDirectoryLockId ? quotavfs::GetVFSName()
@@ -1927,7 +1930,8 @@ Connection::AsyncClone(bool aReadOnly,
// The cloned connection will still implement the synchronous API, but throw
// if any synchronous methods are called on the main thread.
RefPtr<Connection> clone =
new Connection(mStorageService, flags, ASYNCHRONOUS, mTelemetryFilename);
new Connection(mStorageService, flags, ASYNCHRONOUS, mTelemetryFilename,
mInterruptible, mIgnoreLockingMode, mOpenNotExclusive);
RefPtr<AsyncInitializeClone> initEvent =
new AsyncInitializeClone(this, clone, aReadOnly, aCallback);

View File

@@ -86,7 +86,7 @@ class Connection final : public mozIStorageConnection,
Connection(Service* aService, int aFlags,
ConnectionOperation aSupportedOperations,
const nsCString& aTelemetryFilename, bool aInterruptible = false,
bool aIgnoreLockingMode = false);
bool aIgnoreLockingMode = false, bool aOpenNotExclusive = false);
/**
* Creates the connection to an in-memory database.
@@ -480,6 +480,12 @@ class Connection final : public mozIStorageConnection,
*/
const bool mIgnoreLockingMode;
/**
* Stores whether we should ask sqlite3_open_v2 to open without an exclusive
* lock.
*/
const bool mOpenNotExclusive;
/**
* Set to true by Close() or AsyncClose() prior to shutdown.
*

View File

@@ -547,6 +547,8 @@ Service::OpenAsyncDatabase(nsIVariant* aDatabaseStore, uint32_t aOpenFlags,
// Specifying ignoreLockingMode will force use of the readOnly flag:
const bool readOnly =
ignoreLockingMode || (aOpenFlags & mozIStorageService::OPEN_READONLY);
const bool openNotExclusive =
aOpenFlags & mozIStorageService::OPEN_NOT_EXCLUSIVE;
int flags = readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE;
nsCOMPtr<nsIFile> storageFile;
@@ -591,9 +593,9 @@ Service::OpenAsyncDatabase(nsIVariant* aDatabaseStore, uint32_t aOpenFlags,
rv = storageFile->GetNativeLeafName(telemetryFilename);
NS_ENSURE_SUCCESS(rv, rv);
}
RefPtr<Connection> msc =
new Connection(this, flags, Connection::ASYNCHRONOUS, telemetryFilename,
/* interruptible */ true, ignoreLockingMode);
RefPtr<Connection> msc = new Connection(
this, flags, Connection::ASYNCHRONOUS, telemetryFilename,
/* interruptible */ true, ignoreLockingMode, openNotExclusive);
nsCOMPtr<nsIEventTarget> target = msc->getAsyncExecutionTarget();
MOZ_ASSERT(target,
"Cannot initialize a connection that has been closed already");

View File

@@ -1247,6 +1247,9 @@ ConnectionData.prototype = Object.freeze({
* return "false positive" corruption errors if other connections write
* to the DB at the same time.
*
* openNotExclusive -- (bool) Whether to open the database without an exclusive
* lock so the database can be accessed from multiple processes.
*
* vacuumOnIdle -- (bool) Whether to register this connection to be vacuumed
* on idle by the VacuumManager component.
* If you're vacuum-ing an incremental vacuum database, ensure to also
@@ -1384,6 +1387,9 @@ function openConnection(options) {
dbOpenOptions |= Ci.mozIStorageService.OPEN_IGNORE_LOCKING_MODE;
dbOpenOptions |= Ci.mozIStorageService.OPEN_READONLY;
}
if (options.openNotExclusive) {
dbOpenOptions |= Ci.mozIStorageService.OPEN_NOT_EXCLUSIVE;
}
let dbConnectionOptions = Ci.mozIStorageService.CONNECTION_DEFAULT;