Skip to content

Commit 47dc93b

Browse files
committed
Add back lockTimeout; Use new methods.
1 parent 641acb8 commit 47dc93b

15 files changed

+186
-61
lines changed

.github/workflows/test.yaml

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ jobs:
1717
- name: Check formatting
1818
run: dart format --output=none --set-exit-if-changed .
1919
- name: Lint
20-
run: dart analyze
20+
run: dart analyze lib test example
21+
- name: Test Fixes
22+
run: dart fix --compare-to-golden test_fixes
2123
- name: Publish dry-run
2224
run: dart pub publish --dry-run
2325
- name: Check publish score

example/basic_example.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void main() async {
2828
// Combine multiple statements into a single write transaction for:
2929
// 1. Atomic persistence (all updates are either applied or rolled back).
3030
// 2. Improved throughput.
31-
await db.writeTransaction((tx) async {
31+
await db.transaction((tx) async {
3232
await tx.execute('INSERT INTO test_data(data) values(?)', ['Test3']);
3333
await tx.execute('INSERT INTO test_data(data) values(?)', ['Test4']);
3434
});

lib/fix_data.yaml

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# This provides automatic fixes of deprecations using `dart fix`.
2+
# See: https://github.com/flutter/flutter/wiki/Data-driven-Fixes
3+
version: 1
4+
transforms:
5+
- title: 'Rename writeLock to lock'
6+
date: 2024-04-02
7+
element:
8+
uris: ['src/sqlite_connection.dart']
9+
method: 'writeLock'
10+
inClass: 'SqliteConnection'
11+
changes:
12+
- kind: 'rename'
13+
newName: 'lock'
14+
- title: 'Rename writeTransaction to transaction'
15+
date: 2024-04-02
16+
element:
17+
uris: ['src/sqlite_connection.dart']
18+
method: 'writeTransaction'
19+
inClass: 'SqliteConnection'
20+
changes:
21+
- kind: 'rename'
22+
newName: 'transaction'
23+
- title: 'Rename writeLock to lock'
24+
date: 2024-04-02
25+
element:
26+
uris: ['src/sqlite_database.dart']
27+
method: 'writeLock'
28+
inClass: 'SqliteDatabase'
29+
changes:
30+
- kind: 'rename'
31+
newName: 'lock'
32+
- title: 'Rename writeTransaction to transaction'
33+
date: 2024-04-02
34+
element:
35+
uris: ['src/sqlite_database.dart']
36+
method: 'writeTransaction'
37+
inClass: 'SqliteDatabase'
38+
changes:
39+
- kind: 'rename'
40+
newName: 'transaction'
41+
- title: 'Rename readTransaction to transaction'
42+
date: 2024-04-02
43+
element:
44+
uris: ['src/sqlite_database.dart']
45+
method: 'readTransaction'
46+
inClass: 'SqliteDatabase'
47+
changes:
48+
- kind: 'addParameter'
49+
index: 1
50+
name: 'readOnly'
51+
style: required_named
52+
argumentValue:
53+
expression: 'true'
54+
- kind: 'rename'
55+
newName: 'transaction'

lib/src/connection_pool.dart

+33-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import 'update_notification.dart';
1010

1111
/// A connection pool with a single write connection and multiple read connections.
1212
class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
13-
SqliteConnection? _writeConnection;
13+
SqliteConnectionImpl? _writeConnection;
1414

1515
final List<SqliteConnectionImpl> _readConnections = [];
1616

@@ -42,7 +42,7 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
4242
SqliteConnectionPool(this._factory,
4343
{this.updates,
4444
this.maxReaders = 5,
45-
SqliteConnection? writeConnection,
45+
SqliteConnectionImpl? writeConnection,
4646
this.debugName,
4747
required this.mutex,
4848
required SerializedPortClient upstreamPort})
@@ -72,7 +72,7 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
7272
_readConnections.remove(connection);
7373
}
7474
try {
75-
return await connection.readLock((ctx) async {
75+
return await connection.lock((ctx) async {
7676
if (haveLock) {
7777
// Already have a different lock - release this one.
7878
return false;
@@ -91,7 +91,10 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
9191
}
9292

9393
return true;
94-
}, lockTimeout: lockTimeout, debugContext: debugContext);
94+
},
95+
lockTimeout: lockTimeout,
96+
readOnly: true,
97+
debugContext: debugContext);
9598
} on TimeoutException {
9699
return false;
97100
}
@@ -117,6 +120,12 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
117120
@override
118121
Future<T> writeLock<T>(Future<T> Function(SqliteWriteContext tx) callback,
119122
{Duration? lockTimeout, String? debugContext}) {
123+
return _writeLock(callback,
124+
lockTimeout: lockTimeout, debugContext: debugContext, global: true);
125+
}
126+
127+
Future<T> _writeLock<T>(Future<T> Function(SqliteWriteContext tx) callback,
128+
{Duration? lockTimeout, String? debugContext, required bool global}) {
120129
if (closed) {
121130
throw AssertionError('Closed');
122131
}
@@ -132,10 +141,28 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
132141
readOnly: false,
133142
openFactory: _factory);
134143
return _runZoned(() {
144+
if (global) {
145+
// ignore: deprecated_member_use_from_same_package
146+
return _writeConnection!.writeLock(callback,
147+
lockTimeout: lockTimeout, debugContext: debugContext);
148+
} else {
149+
return _writeConnection!.lock(callback,
150+
lockTimeout: lockTimeout, debugContext: debugContext);
151+
}
152+
}, debugContext: debugContext ?? 'execute()');
153+
}
154+
155+
@override
156+
Future<T> lock<T>(Future<T> Function(SqliteWriteContext tx) callback,
157+
{bool? readOnly, Duration? lockTimeout, String? debugContext}) {
158+
if (readOnly == true) {
135159
// ignore: deprecated_member_use_from_same_package
136-
return _writeConnection!.writeLock(callback,
160+
return readLock((ctx) => callback(ctx as SqliteWriteContext),
137161
lockTimeout: lockTimeout, debugContext: debugContext);
138-
}, debugContext: debugContext ?? 'execute()');
162+
} else {
163+
return _writeLock(callback,
164+
lockTimeout: lockTimeout, debugContext: debugContext, global: false);
165+
}
139166
}
140167

141168
/// The [Mutex] on individual connections do already error in recursive locks.

lib/src/sqlite_connection.dart

+9-8
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,18 @@ abstract class SqliteConnection extends SqliteWriteContext {
8282
/// Statements within the transaction must be done on the provided
8383
/// [SqliteWriteContext] - attempting statements on the [SqliteConnection]
8484
/// instance will error.
85+
///
86+
/// [lockTimeout] only controls the timeout for locking the connection.
87+
/// Timeout for database-level locks can be configured on [SqliteOpenOptions].
8588
Future<T> transaction<T>(Future<T> Function(SqliteWriteContext tx) callback,
86-
{bool? readOnly});
89+
{bool? readOnly, Duration? lockTimeout});
8790

8891
/// Open a read-only transaction.
8992
///
9093
/// Statements within the transaction must be done on the provided
9194
/// [SqliteReadContext] - attempting statements on the [SqliteConnection]
9295
/// instance will error.
93-
@Deprecated(
94-
'Use [transaction(callback)] instead, and set lockTimeout on [SqliteOpenOptions].')
96+
@Deprecated('Use [transaction(callback)] instead.')
9597
Future<T> readTransaction<T>(
9698
Future<T> Function(SqliteReadContext tx) callback,
9799
{Duration? lockTimeout});
@@ -104,8 +106,7 @@ abstract class SqliteConnection extends SqliteWriteContext {
104106
/// Statements within the transaction must be done on the provided
105107
/// [SqliteWriteContext] - attempting statements on the [SqliteConnection]
106108
/// instance will error.
107-
@Deprecated(
108-
'Use [transaction(callback)] instead, and set lockTimeout on [SqliteOpenOptions].')
109+
@Deprecated('Use [transaction(callback)] instead.')
109110
Future<T> writeTransaction<T>(
110111
Future<T> Function(SqliteWriteContext tx) callback,
111112
{Duration? lockTimeout});
@@ -122,14 +123,14 @@ abstract class SqliteConnection extends SqliteWriteContext {
122123
/// Takes a read lock on this connection, without starting a transaction.
123124
///
124125
/// This is a low-level API. In most cases, [transaction] should be used instead.
125-
@Deprecated('Use [lock] instead, and set lockTimeout on [SqliteOpenOptions].')
126+
@Deprecated('Use [lock] instead.')
126127
Future<T> readLock<T>(Future<T> Function(SqliteReadContext tx) callback,
127128
{Duration? lockTimeout, String? debugContext});
128129

129130
/// Takes a global write lock, without starting a transaction.
130131
///
131132
/// This is a low-level API. In most cases, [transaction] should be used instead.
132-
@Deprecated('Use [lock] instead, and set lockTimeout on [SqliteOpenOptions].')
133+
@Deprecated('Use [lock] instead.')
133134
Future<T> writeLock<T>(Future<T> Function(SqliteWriteContext tx) callback,
134135
{Duration? lockTimeout, String? debugContext});
135136

@@ -142,7 +143,7 @@ abstract class SqliteConnection extends SqliteWriteContext {
142143
///
143144
/// Any direct query methods such as [getAll] or [execute] uses [lock] internally.
144145
Future<T> lock<T>(Future<T> Function(SqliteWriteContext tx) callback,
145-
{bool? readOnly, String? debugContext});
146+
{bool? readOnly, Duration? lockTimeout, String? debugContext});
146147

147148
Future<void> close();
148149

lib/src/sqlite_connection_impl.dart

+8-2
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ class SqliteConnectionImpl with SqliteQueries implements SqliteConnection {
106106
}
107107

108108
@override
109-
Future<T> readLock<T>(Future<T> Function(SqliteReadContext tx) callback,
110-
{Duration? lockTimeout, String? debugContext}) async {
109+
Future<T> lock<T>(Future<T> Function(SqliteWriteContext tx) callback,
110+
{bool? readOnly, Duration? lockTimeout, String? debugContext}) async {
111111
// Private lock to synchronize this with other statements on the same connection,
112112
// to ensure that transactions aren't interleaved.
113113
return _connectionMutex.lock(() async {
@@ -120,6 +120,12 @@ class SqliteConnectionImpl with SqliteQueries implements SqliteConnection {
120120
}, timeout: lockTimeout);
121121
}
122122

123+
@override
124+
Future<T> readLock<T>(Future<T> Function(SqliteReadContext tx) callback,
125+
{Duration? lockTimeout, String? debugContext}) async {
126+
return lock(callback, lockTimeout: lockTimeout, debugContext: debugContext);
127+
}
128+
123129
@override
124130
Future<T> writeLock<T>(Future<T> Function(SqliteWriteContext tx) callback,
125131
{Duration? lockTimeout, String? debugContext}) async {

lib/src/sqlite_database.dart

+9-4
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,12 @@ class SqliteDatabase with SqliteQueries implements SqliteConnection {
199199
/// Changes from any write transaction are not visible to read transactions
200200
/// started before it.
201201
@override
202+
@Deprecated('Use [transaction] instead.')
202203
Future<T> readTransaction<T>(
203204
Future<T> Function(SqliteReadContext tx) callback,
204205
{Duration? lockTimeout}) {
205-
return _pool.readTransaction(callback, lockTimeout: lockTimeout);
206+
return _pool.transaction(callback,
207+
readOnly: true, lockTimeout: lockTimeout);
206208
}
207209

208210
/// Open a read-write transaction.
@@ -213,20 +215,23 @@ class SqliteDatabase with SqliteQueries implements SqliteConnection {
213215
/// The write transaction is automatically committed when the callback finishes,
214216
/// or rolled back on any error.
215217
@override
218+
@Deprecated('Use [transaction] instead.')
216219
Future<T> writeTransaction<T>(
217220
Future<T> Function(SqliteWriteContext tx) callback,
218221
{Duration? lockTimeout}) {
219-
return _pool.writeTransaction(callback, lockTimeout: lockTimeout);
222+
return _pool.transaction(callback, lockTimeout: lockTimeout);
220223
}
221224

222225
@override
226+
@Deprecated('Use [lock] instead.')
223227
Future<T> readLock<T>(Future<T> Function(SqliteReadContext tx) callback,
224228
{Duration? lockTimeout, String? debugContext}) {
225-
return _pool.readLock(callback,
226-
lockTimeout: lockTimeout, debugContext: debugContext);
229+
return _pool.lock(callback,
230+
readOnly: true, lockTimeout: lockTimeout, debugContext: debugContext);
227231
}
228232

229233
@override
234+
@Deprecated('Use [lock] instead.')
230235
Future<T> writeLock<T>(Future<T> Function(SqliteWriteContext tx) callback,
231236
{Duration? lockTimeout, String? debugContext}) {
232237
return _pool.writeLock(callback,

lib/src/sqlite_queries.dart

+8-6
Original file line numberDiff line numberDiff line change
@@ -144,26 +144,28 @@ mixin SqliteQueries implements SqliteWriteContext, SqliteConnection {
144144

145145
@override
146146
Future<T> lock<T>(Future<T> Function(SqliteWriteContext tx) callback,
147-
{bool? readOnly, String? debugContext}) {
147+
{bool? readOnly, Duration? lockTimeout, String? debugContext}) {
148148
if (readOnly == true) {
149149
// ignore: deprecated_member_use_from_same_package
150150
return readLock((ctx) => callback(ctx as SqliteWriteContext),
151-
debugContext: debugContext);
151+
lockTimeout: lockTimeout, debugContext: debugContext);
152152
} else {
153153
// ignore: deprecated_member_use_from_same_package
154-
return writeLock(callback, debugContext: debugContext);
154+
return writeLock(callback,
155+
lockTimeout: lockTimeout, debugContext: debugContext);
155156
}
156157
}
157158

158159
@override
159160
Future<T> transaction<T>(Future<T> Function(SqliteWriteContext tx) callback,
160-
{bool? readOnly}) {
161+
{bool? readOnly, Duration? lockTimeout}) {
161162
if (readOnly == true) {
162163
// ignore: deprecated_member_use_from_same_package
163-
return readTransaction((ctx) => callback(ctx as SqliteWriteContext));
164+
return readTransaction((ctx) => callback(ctx as SqliteWriteContext),
165+
lockTimeout: lockTimeout);
164166
} else {
165167
// ignore: deprecated_member_use_from_same_package
166-
return writeTransaction(callback);
168+
return writeTransaction(callback, lockTimeout: lockTimeout);
167169
}
168170
}
169171
}

scripts/benchmark.dart

+9-8
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class SqliteBenchmark {
2525
List<SqliteBenchmark> benchmarks = [
2626
SqliteBenchmark('Insert: JSON1',
2727
(SqliteDatabase db, List<List<String>> parameters) async {
28-
await db.writeTransaction((tx) async {
28+
await db.transaction((tx) async {
2929
for (var i = 0; i < parameters.length; i += 5000) {
3030
var sublist = parameters.sublist(i, min(parameters.length, i + 5000));
3131
await tx.execute(
@@ -37,14 +37,14 @@ List<SqliteBenchmark> benchmarks = [
3737
}, maxBatchSize: 20000),
3838
SqliteBenchmark('Read: JSON1',
3939
(SqliteDatabase db, List<List<String>> parameters) async {
40-
await db.readTransaction((tx) async {
40+
await db.transaction((tx) async {
4141
for (var i = 0; i < parameters.length; i += 10000) {
4242
var sublist = List.generate(10000, (index) => index);
4343
await tx.getAll(
4444
'SELECT name, email FROM customers WHERE id IN (SELECT e.value FROM json_each(?) e)',
4545
[jsonEncode(sublist)]);
4646
}
47-
});
47+
}, readOnly: true);
4848
}, maxBatchSize: 200000, enabled: false),
4949
SqliteBenchmark('lock in isolate',
5050
(SqliteDatabase db, List<List<String>> parameters) async {
@@ -61,13 +61,14 @@ List<SqliteBenchmark> benchmarks = [
6161
SqliteBenchmark('Write lock',
6262
(SqliteDatabase db, List<List<String>> parameters) async {
6363
for (var _ in parameters) {
64+
// ignore: deprecated_member_use_from_same_package
6465
await db.writeLock((tx) async {});
6566
}
6667
}, maxBatchSize: 5000, enabled: false),
6768
SqliteBenchmark('Read lock',
6869
(SqliteDatabase db, List<List<String>> parameters) async {
6970
for (var _ in parameters) {
70-
await db.readLock((tx) async {});
71+
await db.lock((tx) async {}, readOnly: true);
7172
}
7273
}, maxBatchSize: 5000, enabled: false),
7374
SqliteBenchmark('Insert: Direct',
@@ -79,7 +80,7 @@ List<SqliteBenchmark> benchmarks = [
7980
}, maxBatchSize: 500),
8081
SqliteBenchmark('Insert: writeTransaction',
8182
(SqliteDatabase db, List<List<String>> parameters) async {
82-
await db.writeTransaction((tx) async {
83+
await db.transaction((tx) async {
8384
for (var params in parameters) {
8485
await tx.execute(
8586
'INSERT INTO customers(name, email) VALUES(?, ?)', params);
@@ -110,7 +111,7 @@ List<SqliteBenchmark> benchmarks = [
110111
}, maxBatchSize: 2000),
111112
SqliteBenchmark('Insert: writeTransaction no await',
112113
(SqliteDatabase db, List<List<String>> parameters) async {
113-
await db.writeTransaction((tx) async {
114+
await db.transaction((tx) async {
114115
for (var params in parameters) {
115116
tx.execute('INSERT INTO customers(name, email) VALUES(?, ?)', params);
116117
}
@@ -139,7 +140,7 @@ List<SqliteBenchmark> benchmarks = [
139140
}),
140141
SqliteBenchmark('Insert: executeBatch',
141142
(SqliteDatabase db, List<List<String>> parameters) async {
142-
await db.writeTransaction((tx) async {
143+
await db.transaction((tx) async {
143144
await tx.executeBatch(
144145
'INSERT INTO customers(name, email) VALUES(?, ?)', parameters);
145146
});
@@ -167,7 +168,7 @@ void main() async {
167168
20000, (index) => ['Test user $index', 'user$index@example.org']);
168169

169170
createTables(SqliteDatabase db) async {
170-
await db.writeTransaction((tx) async {
171+
await db.transaction((tx) async {
171172
await tx.execute('DROP TABLE IF EXISTS customers');
172173
await tx.execute(
173174
'CREATE TABLE customers(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, email TEXT)');

0 commit comments

Comments
 (0)