exclusively<T> method
Obtains an exclusive lock on the current database context, runs action
in it and then releases the lock.
This obtains a local lock on the underlying executor
without starting a
transaction or coordinating with other processes on the same database.
It is possible to start a transaction
within an exclusively
block.
When exclusively
is called on a database connected to a remote isolate
or a shared web worker, other isolates and tabs will be blocked on the
database until the returned future completes.
With sqlite3, exclusively
is useful to set certain pragmas like
foreign_keys
which can't be done in a transaction for a limited scope.
For instance, some migrations may look like this:
await exclusively(() async {
await customStatement('pragma foreign_keys = OFF;');
await transaction(() async {
// complex updates or migrations temporarily breaking foreign
// references...
});
await customStatement('pragma foreign_keys = OFF;');
});
If the exclusively
block had been omitted from the previous snippet,
it would have been possible for other concurrent database calls to occur
between the transaction and the pragma
statements.
Outside of blocks requiring exclusive access to set pragmas not supported
in transactions, consider using transaction
instead of exclusively
.
Transactions also take exclusive control over the database, but they also
are atomic (either all statements in a transaction complete or none at
all), whereas an error in an exclusively
block does not roll back
earlier statements.
Implementation
Future<T> exclusively<T>(Future<T> Function() action) async {
return await resolvedEngine.doWhenOpened((executor) {
final exclusive = executor.beginExclusive();
return _runConnectionZoned(
_ExclusiveExecutor(this, executor: exclusive),
() async {
await exclusive.ensureOpen(attachedDatabase);
try {
return await action();
} finally {
exclusive.close();
}
},
);
});
}