Skip to content

Commit c5f4eaf

Browse files
authored
Prepare dev update (#136)
1 parent 4ef63a6 commit c5f4eaf

13 files changed

Lines changed: 99 additions & 70 deletions

File tree

packages/drift_sqlite_async/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## 0.3.0-wip
1+
## 0.3.0-wip.0
22

33
- Support versions 3.x of the `sqlite3` package.
44

packages/drift_sqlite_async/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: drift_sqlite_async
2-
version: 0.3.0-wip
2+
version: 0.3.0-wip.0
33
homepage: https://github.com/powersync-ja/sqlite_async.dart
44
repository: https://github.com/powersync-ja/sqlite_async.dart
55
description: Use Drift with a sqlite_async database, allowing both to be used in the same application.

packages/sqlite_async/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
## 0.14.0-wip
1+
## 0.14.0-wip.0
2+
3+
__Note__: This version of `sqlite_async` is still in development and there might be additional
4+
API changes between this release and the final `0.14.0` version. This release is mostly meant for
5+
internal testing.
26

37
- Support versions 3.x of the `sqlite3` package and 0.6.0 of `sqlite3_web`.
48
- Remove the `sqlite3_open.dart` library, SQLite libraries are no longer loaded through Dart.

packages/sqlite_async/lib/src/common/connection/sync_sqlite_connection.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:sqlite_async/src/update_notification.dart';
88
import 'package:sqlite_async/src/utils/profiler.dart';
99

1010
import '../../impl/context.dart';
11+
import '../../utils/shared_utils.dart';
1112

1213
/// A simple "synchronous" connection which provides the async SqliteConnection
1314
/// implementation using a synchronous SQLite connection
@@ -35,6 +36,16 @@ final class SyncSqliteConnection extends SqliteConnection {
3536
);
3637
}
3738

39+
@override
40+
Future<T> readTransaction<T>(
41+
Future<T> Function(SqliteReadContext tx) callback,
42+
{Duration? lockTimeout}) {
43+
return readLock((ctx) async {
44+
return await internalReadTransaction(ctx, callback,
45+
isDedicatedReadConnection: false);
46+
}, lockTimeout: lockTimeout, debugContext: 'readTransaction()');
47+
}
48+
3849
@override
3950
Future<T> abortableReadLock<T>(
4051
Future<T> Function(SqliteReadContext tx) callback,

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:sqlite_async/src/sqlite_options.dart';
77
import 'package:sqlite_async/src/sqlite_connection.dart';
88

99
import '../impl/platform.dart' as platform;
10+
import '../utils/shared_utils.dart';
1011

1112
/// A SQLite database instance.
1213
///
@@ -96,4 +97,14 @@ abstract base class SqliteDatabase extends SqliteConnection {
9697
@internal
9798
abstract base class SqliteDatabaseImpl extends SqliteDatabase {
9899
SqliteDatabaseImpl() : super._();
100+
101+
@override
102+
Future<T> readTransaction<T>(
103+
Future<T> Function(SqliteReadContext tx) callback,
104+
{Duration? lockTimeout}) {
105+
return readLock((ctx) async {
106+
return await internalReadTransaction(ctx, callback,
107+
isDedicatedReadConnection: maxReaders > 0);
108+
}, lockTimeout: lockTimeout, debugContext: 'readTransaction()');
109+
}
99110
}

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

Lines changed: 14 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,16 @@ import 'worker.dart';
3030
final class NativeSqliteDatabaseImpl extends SqliteDatabaseImpl {
3131
@override
3232
final NativeSqliteOpenFactory openFactory;
33-
late final Future<SqliteConnectionPool> _pool = _openNativePool(openFactory);
33+
late final Future<SqliteConnectionPool> _pool =
34+
_openNativePool(openFactory).then((pool) {
35+
// Pipe updates into a stream controller as soon as the pool is opened to
36+
// avoid missing updates if the stream is only listened to later.
37+
_updates.addStream(pool.updatedTables.map((e) {
38+
return UpdateNotification(e.toSet());
39+
}));
40+
41+
return pool;
42+
});
3443
bool _isClosed = false;
3544
final _lockGuard = Object();
3645

@@ -43,10 +52,11 @@ final class NativeSqliteDatabaseImpl extends SqliteDatabaseImpl {
4352

4453
final Queue<IsolateWorker> _workers;
4554

55+
final StreamController<UpdateNotification> _updates =
56+
StreamController.broadcast(sync: true);
57+
4658
@override
47-
late final Stream<UpdateNotification> updates = Stream.fromFuture(_pool)
48-
.asyncExpand((pool) => pool.updatedTables
49-
.map((changedTables) => UpdateNotification(changedTables.toSet())));
59+
Stream<UpdateNotification> get updates => _updates.stream;
5060

5161
NativeSqliteDatabaseImpl(this.openFactory)
5262
:
@@ -84,58 +94,6 @@ final class NativeSqliteDatabaseImpl extends SqliteDatabaseImpl {
8494
}
8595
}
8696

87-
/// Open a read-only transaction.
88-
///
89-
/// Up to [maxReaders] read transactions can run concurrently.
90-
/// After that, read transactions are queued.
91-
///
92-
/// Read transactions can run concurrently to a write transaction.
93-
///
94-
/// Changes from any write transaction are not visible to read transactions
95-
/// started before it.
96-
@override
97-
Future<T> readTransaction<T>(
98-
Future<T> Function(SqliteReadContext tx) callback,
99-
{Duration? lockTimeout}) async {
100-
return _useConnection(
101-
writer: false,
102-
abortTrigger: lockTimeout?.asTimeout,
103-
debugContext: 'readTransaction',
104-
(context) {
105-
return _transactionInLease(context, callback);
106-
},
107-
);
108-
}
109-
110-
/// Open a read-write transaction.
111-
///
112-
/// Only a single write transaction can run at a time - any concurrent
113-
/// transactions are queued.
114-
///
115-
/// The write transaction is automatically committed when the callback finishes,
116-
/// or rolled back on any error.
117-
@override
118-
Future<T> writeTransaction<T>(
119-
Future<T> Function(SqliteWriteContext tx) callback,
120-
{Duration? lockTimeout}) {
121-
return _useConnection(
122-
writer: true,
123-
abortTrigger: lockTimeout?.asTimeout,
124-
debugContext: 'writeTransaction',
125-
(context) {
126-
return _transactionInLease(context, callback);
127-
},
128-
);
129-
}
130-
131-
Future<T> _transactionInLease<T>(
132-
_LeasedContext context,
133-
Future<T> Function(SqliteWriteContext tx) callback,
134-
) {
135-
final ctx = ScopedWriteContext(context);
136-
return ctx.writeTransaction(callback).whenComplete(ctx.invalidate);
137-
}
138-
13997
@override
14098
Future<T> abortableReadLock<T>(
14199
Future<T> Function(SqliteReadContext tx) callback,

packages/sqlite_async/lib/src/sqlite_connection.dart

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,7 @@ abstract class SqliteConnection implements SqliteWriteContext {
136136
/// instance will error.
137137
Future<T> readTransaction<T>(
138138
Future<T> Function(SqliteReadContext tx) callback,
139-
{Duration? lockTimeout}) {
140-
return readLock((ctx) async {
141-
return await internalReadTransaction(ctx, callback);
142-
}, lockTimeout: lockTimeout, debugContext: 'readTransaction()');
143-
}
139+
{Duration? lockTimeout});
144140

145141
/// Open a read-write transaction.
146142
///

packages/sqlite_async/lib/src/utils/shared_utils.dart

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,35 @@ import 'package:sqlite3/common.dart';
55

66
import '../sqlite_connection.dart';
77

8-
Future<T> internalReadTransaction<T>(SqliteReadContext ctx,
9-
Future<T> Function(SqliteReadContext tx) callback) async {
8+
Future<T> internalReadTransaction<T>(
9+
SqliteReadContext ctx,
10+
Future<T> Function(SqliteReadContext tx) callback, {
11+
required bool isDedicatedReadConnection,
12+
}) async {
1013
try {
11-
await ctx.getAll('BEGIN');
14+
// We want read transactions to observe the state of the database at the
15+
// time they've been opened. By default however, SQLite only starts the
16+
// transaction on the first statement (BEGIN just sets a bit to disable
17+
// autocommit). This is a problem for snippets like:
18+
//
19+
// await db.readTransaction((tx) async {
20+
// // point in time 1.
21+
// await longDelay();
22+
// await readFromDb(tx); // should read state from point in time 1!
23+
// });
24+
//
25+
// With a write concurrent to `longDelay()`, the actual transaction would
26+
// start too late and observe state from after it was opened in Dart. This
27+
// is why we use BEGIN IMMEDIATE instead of BEGIN. However, we only need
28+
// this for read connections: If the database "pool" is backed by a single
29+
// connection (e.g. on the web), using BEGIN IMMEDIATE would be fairly
30+
// expensive for a read. A concurrent write wouldn't be a concern there
31+
// because the read context blocks the single connection.
32+
// Either way, this is not a consistency issue. Transactions observe a
33+
// single consistent snapshot either way, we just want them to observe an
34+
// earlier snapshot in some cases.
35+
36+
await ctx.getAll(isDedicatedReadConnection ? 'BEGIN IMMEDIATE' : 'BEGIN');
1237
final result = await callback(ctx);
1338
await ctx.getAll('END TRANSACTION');
1439
return result;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ final class WebDatabase extends SqliteDatabaseImpl
6868
@override
6969

7070
/// Not supported on web. There is only 1 connection.
71-
int get maxReaders => throw UnimplementedError();
71+
int get maxReaders => 0;
7272

7373
@override
7474

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ final class AsyncWebDatabaseImpl extends SqliteDatabaseImpl
2929
late Stream<UpdateNotification> updates;
3030

3131
@override
32-
int get maxReaders => openFactory.sqliteOptions.maxReaders;
32+
int get maxReaders => 0;
3333

3434
@override
3535
@protected

0 commit comments

Comments
 (0)