Skip to content

Commit fe0b71d

Browse files
committed
test
1 parent ea41b34 commit fe0b71d

File tree

8 files changed

+88
-17
lines changed

8 files changed

+88
-17
lines changed

packages/sqlite_async/lib/src/common/sqlite_database.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ mixin SqliteDatabaseMixin implements SqliteConnection, SqliteQueries {
3939
///
4040
/// Use this to access the database in background isolates.
4141
IsolateConnectionFactory isolateConnectionFactory();
42+
43+
/// Locks all underlying connections making up this database, and gives [block] access to all of them at once.
44+
Future<T> withAllConnections<T>(
45+
Future<T> Function(
46+
SqliteWriteContext writer, List<SqliteReadContext> readers)
47+
block);
4248
}
4349

4450
/// A SQLite database instance.

packages/sqlite_async/lib/src/impl/single_connection_database.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,9 @@ final class SingleConnectionDatabase
6262
List<SqliteConnection> get allConnections {
6363
return [connection];
6464
}
65+
66+
@override
67+
Future<T> withAllConnections<T>(Future<T> Function(SqliteWriteContext writer, List<SqliteReadContext> readers) block) {
68+
return writeLock((_) => block(connection, []));
69+
}
6570
}

packages/sqlite_async/lib/src/impl/stub_sqlite_database.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,9 @@ class SqliteDatabaseImpl
6969
List<SqliteConnection> get allConnections {
7070
throw UnimplementedError();
7171
}
72+
73+
@override
74+
Future<T> withAllConnections<T>(Future<T> Function(SqliteWriteContext writer, List<SqliteReadContext> readers) block) {
75+
throw UnimplementedError();
76+
}
7277
}

packages/sqlite_async/lib/src/native/database/connection_pool.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,11 +284,12 @@ class SqliteConnectionPool with SqliteQueries implements SqliteConnection {
284284
}
285285

286286
// Wait after all locks are taken
287-
final contexts = await Future.wait([
287+
final [writer as SqliteWriteContext, ...readers] = await Future.wait([
288288
writeLockedCompleter.future,
289289
...readLockedCompleters.map((e) => e.future)
290290
]);
291-
return (contexts.first as SqliteWriteContext, contexts.sublist(1));
291+
292+
return (writer, readers);
292293
}
293294
}
294295

packages/sqlite_async/lib/src/native/database/native_sqlite_database.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,9 @@ class SqliteDatabaseImpl
176176
List<SqliteConnection> get allConnections {
177177
return _pool.allConnections;
178178
}
179+
180+
@override
181+
Future<T> withAllConnections<T>(Future<T> Function(SqliteWriteContext writer, List<SqliteReadContext> readers) block) {
182+
return _pool.withAllConnections(block);
183+
}
179184
}

packages/sqlite_async/lib/src/web/database.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,14 @@ class WebDatabase
176176
List<SqliteConnection> get allConnections {
177177
return [this];
178178
}
179+
180+
@override
181+
Future<T> withAllConnections<T>(
182+
Future<T> Function(
183+
SqliteWriteContext writer, List<SqliteReadContext> readers)
184+
block) {
185+
return writeLock((_) => block(this, []));
186+
}
179187
}
180188

181189
final class _UnscopedContext extends UnscopedContext {

packages/sqlite_async/lib/src/web/database/web_sqlite_database.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,12 @@ class SqliteDatabaseImpl
183183
List<SqliteConnection> get allConnections {
184184
return [_connection];
185185
}
186+
187+
@override
188+
Future<T> withAllConnections<T>(
189+
Future<T> Function(
190+
SqliteWriteContext writer, List<SqliteReadContext> readers)
191+
block) {
192+
return writeLock((_) => block(_connection, []));
193+
}
186194
}

packages/sqlite_async/test/native/basic_test.dart

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ library;
44
import 'dart:async';
55
import 'dart:math';
66

7+
import 'package:collection/collection.dart';
78
import 'package:sqlite3/common.dart' as sqlite;
9+
import 'package:sqlite3/sqlite3.dart' show Row;
810
import 'package:sqlite_async/sqlite_async.dart';
911
import 'package:test/test.dart';
1012

@@ -76,25 +78,56 @@ void main() {
7678
await db.initialize();
7779
await createTables(db);
7880

79-
print("${DateTime.now()} start");
8081

81-
final withAllConnsFut = () async {
82-
await Future.delayed(const Duration(milliseconds: 20));
83-
await db.withAllConnections((writer, readers) async {
84-
print("${DateTime.now()} in withAllConnections");
85-
await Future.delayed(const Duration(seconds: 5));
86-
});
87-
}();
82+
Future<Row> readWithRandomDelay(SqliteReadContext ctx, int id) async {
83+
return await ctx.get(
84+
'SELECT ? as i, test_sleep(?) as sleep, test_connection_name() as connection',
85+
[id, 5 + Random().nextInt(10)]);
86+
}
8887

89-
var readFutures = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((i) => db.get(
90-
'SELECT ? as i, test_sleep(?) as sleep, test_connection_name() as connection',
91-
[i, 5 + Random().nextInt(10)]));
88+
// Warm up to spawn the max readers
89+
await Future.wait(
90+
[1, 2, 3, 4, 5, 6, 7, 8].map((i) => readWithRandomDelay(db, i)),
91+
);
9292

93-
final futures = [...readFutures, withAllConnsFut];
93+
bool finishedWithAllConns = false;
9494

95-
await for (var result in Stream.fromFutures(futures)) {
96-
print("${DateTime.now()} $result");
97-
}
95+
late Future<void> readsCalledWhileWithAllConnsRunning;
96+
97+
print("${DateTime.now()} start");
98+
await db.withAllConnections((writer, readers) async {
99+
assert(readers.length == 3);
100+
101+
// Run some reads during the block that they should run after the block finishes and releases
102+
// all locks
103+
readsCalledWhileWithAllConnsRunning = Future.wait(
104+
[1, 2, 3, 4, 5, 6, 7, 8].map((i) async {
105+
final r = await db.readLock((c) async {
106+
expect(finishedWithAllConns, isTrue);
107+
return await readWithRandomDelay(c, i);
108+
});
109+
print(
110+
"${DateTime.now()} After withAllConnections, started while running $r");
111+
}),
112+
);
113+
114+
await Future.wait([
115+
writer.execute(
116+
"INSERT OR REPLACE INTO test_data(id, description) SELECT ? as i, test_sleep(?) || ' ' || test_connection_name() || ' 1 ' || datetime() as connection RETURNING *",
117+
[
118+
123,
119+
5 + Random().nextInt(20)
120+
]).then((value) =>
121+
print("${DateTime.now()} withAllConnections writer done $value")),
122+
...readers
123+
.mapIndexed((i, r) => readWithRandomDelay(r, i).then((results) {
124+
print(
125+
"${DateTime.now()} withAllConnections readers done $results");
126+
}))
127+
]);
128+
}).then((_) => finishedWithAllConns = true);
129+
130+
await readsCalledWhileWithAllConnsRunning;
98131
});
99132

100133
test('Concurren 2', () async {

0 commit comments

Comments
 (0)