chore: Prepare the release of 0.0.4 with serverpod-4.0.0-rc.1 support (#113)
This commit is contained in:
parent
852c81c64a
commit
fbad5c61fe
140 changed files with 22326 additions and 805 deletions
5
.github/actions/setup/action.yml
vendored
5
.github/actions/setup/action.yml
vendored
|
|
@ -5,6 +5,11 @@ runs:
|
|||
using: composite
|
||||
steps:
|
||||
- uses: dart-lang/setup-dart@v1
|
||||
with:
|
||||
sdk: 3.12.2
|
||||
# Composite actions set GITHUB_ACTION_PATH to this folder, so the
|
||||
# default matcher looks for dart-analyzer.json here and fails setup.
|
||||
problem-matcher: false
|
||||
|
||||
- shell: bash
|
||||
run: dart pub global activate melos
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
## 0.0.4
|
||||
|
||||
- chore: Updated Serverpod to `4.0.0-rc.1`.
|
||||
|
||||
## 0.0.3
|
||||
|
||||
- fix: Requires non-nullable foreign keys to be `deferred`.
|
||||
- fix: Rejects `onDelete=Restrict` on synced tables in favor of `onDelete=NoAction`.
|
||||
- fix: Throws proper `DatabaseException` instead of bare `Exception` on the database.
|
||||
- chore: Updated Serverpod to `4.0.0-beta.4`.
|
||||
- chore: Updated Serverpod to `4.0.0-rc.1`.
|
||||
|
||||
## 0.0.2
|
||||
|
||||
|
|
|
|||
41
README.md
41
README.md
|
|
@ -101,48 +101,47 @@ Add the packages to the server and client `pubspec.yaml` files.
|
|||
```yaml
|
||||
# your_project_client/pubspec.yaml
|
||||
dependencies:
|
||||
serverpod_offline_sync_client: 0.0.3
|
||||
serverpod_offline_sync_client: 0.0.4
|
||||
```
|
||||
|
||||
```yaml
|
||||
# your_project_server/pubspec.yaml
|
||||
dependencies:
|
||||
serverpod_offline_sync_server: 0.0.3
|
||||
serverpod_offline_sync_server: 0.0.4
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Version `0.0.3` requires Serverpod `4.0.0-beta.4`.
|
||||
> Version `0.0.4` requires Serverpod `4.0.0-rc.1`.
|
||||
|
||||
After adding the dependencies, list the `serverpod_offline_sync` module on the
|
||||
`generator.yaml` file:
|
||||
`generator.yaml` file and enable the experimental `database: sync` option:
|
||||
|
||||
```yaml
|
||||
modules:
|
||||
serverpod_offline_sync:
|
||||
nickname: offline_sync
|
||||
|
||||
experimental_features:
|
||||
databaseSync: true
|
||||
```
|
||||
|
||||
The next call to `serverpod generate` and `serverpod create-migration` will
|
||||
include the `offline_sync` module in the project. Note that the client tables
|
||||
will only be present on the client's migration if at least one model on the
|
||||
project is defined with `database: all` or `database: client`.
|
||||
project is defined with `database: sync`, `database: all`, or `database: client`.
|
||||
|
||||
### 2. Prepare the synced tables
|
||||
|
||||
Each model you want to sync must be defined with `database: all` (so it exists
|
||||
on both the client and the server), have a UUID primary key, and declare a
|
||||
`scopeId` field. You mostly never set `scopeId` nor see its value on reads; it
|
||||
is maintained by the CRDT sync layer.
|
||||
Each model you want to sync must be defined with `database: sync`. That generates
|
||||
the table on both the client and the server, uses a UUID primary key, and
|
||||
injects the `scopeId` ownership field. You mostly never set `scopeId` nor see
|
||||
its value on reads; it is maintained by the CRDT sync layer.
|
||||
|
||||
```yaml
|
||||
class: Person
|
||||
table: person
|
||||
database: all
|
||||
database: sync
|
||||
fields:
|
||||
id: UuidValue?, defaultPersist=random_v7
|
||||
### Owner scope of this row. Maintained by the CRDT sync layer.
|
||||
### The user never sets this field nor sees its value on reads.
|
||||
scopeId: int?, relation(parent=crdt_scopes, onDelete=Cascade)
|
||||
name: String
|
||||
```
|
||||
|
||||
|
|
@ -263,13 +262,15 @@ sync session for changes to take effect.
|
|||
### Data modeling limitations
|
||||
|
||||
Because of the nature of merge conflicts, the package imposes some data-modeling
|
||||
limitations, enforced at runtime during initialization. Most are fundamental to
|
||||
the design and can never be lifted.
|
||||
limitations. With `database: sync`, Serverpod reports most of them at generate
|
||||
time. The rest are enforced at runtime during initialization. Most are
|
||||
fundamental to the design and can never be lifted.
|
||||
|
||||
- Every synced table must:
|
||||
- Exist on both the client and the server with `database: all`.
|
||||
- Have a UUID primary key.
|
||||
- Declare the field `scopeId: int?, relation(parent=crdt_scopes, onDelete=Cascade)`.
|
||||
- Every synced table must be defined with `database: sync`.
|
||||
- Will exist on both the client and the server, like `database: all`.
|
||||
- The type of the `id` will be `UuidValue` with `defaultPersist=random_v7`.
|
||||
- A `scopeId` field will be added as a cascade relation to `crdt_scopes`.
|
||||
- Both `id` and `scopeId` can be manually declared for more control.
|
||||
- Unique indexes must include `scopeId` together with the target columns.
|
||||
- Global unique indexes are unsupported, except for FK-only indexes.
|
||||
- Unique indexes are only supported with at least one
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ description: Local CRDT vs baseline performance impact measurement.
|
|||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: '^3.10.3'
|
||||
sdk: '^3.12.2'
|
||||
|
||||
resolution: workspace
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ dependencies:
|
|||
benchmark_harness: ^2.4.0
|
||||
intl: ^0.20.2
|
||||
path: ^1.9.1
|
||||
serverpod_database: 4.0.0-beta.4
|
||||
serverpod_database: 4.0.0-rc.1
|
||||
serverpod_offline_sync:
|
||||
path: ../packages/serverpod_offline_sync
|
||||
serverpod_offline_sync_server:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ publish_to: 'none'
|
|||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ^3.10.3
|
||||
sdk: '^3.12.2'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
|
@ -16,7 +16,7 @@ dependencies:
|
|||
http: ^1.5.0
|
||||
path: ^1.9.1
|
||||
path_provider: ^2.1.5
|
||||
serverpod_database: 4.0.0-beta.4
|
||||
serverpod_database: 4.0.0-rc.1
|
||||
serverpod_offline_sync_client:
|
||||
path: ../../packages/serverpod_offline_sync_client
|
||||
serverpod_offline_sync_test_client:
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ typedef CrdtSyncTransport =
|
|||
class CrdtSyncSession {
|
||||
CrdtSyncSession._({
|
||||
required this.done,
|
||||
required Future<void> Function() cancel,
|
||||
}) : _cancel = cancel;
|
||||
required this._cancel,
|
||||
});
|
||||
|
||||
/// Completes when the sync session ends normally or after [cancel].
|
||||
final Future<void> done;
|
||||
|
|
|
|||
|
|
@ -55,18 +55,16 @@ bool _grantsEqual(List<CrdtScopeGrant>? a, List<CrdtScopeGrant> b) {
|
|||
/// control flow); this owns the scopes. It needs only a [DatabaseSession] and
|
||||
/// the membership helpers, so it carries no protocol or transport concerns.
|
||||
class CrdtScopeSyncSession {
|
||||
/// Creates a scope session for [userId] acting in [mode] against [_session].
|
||||
/// Creates a scope session for [_userId] acting in [_mode] against [_session].
|
||||
///
|
||||
/// [peerNodeId] is the remote peer's CRDT node, learned from its connect
|
||||
/// [_peerNodeId] is the remote peer's CRDT node, learned from its connect
|
||||
/// handshake before this session is constructed.
|
||||
CrdtScopeSyncSession(
|
||||
this._session, {
|
||||
required UuidValue userId,
|
||||
required CrdtSyncPeerMode mode,
|
||||
required UuidValue peerNodeId,
|
||||
}) : _userId = userId,
|
||||
_mode = mode,
|
||||
_peerNodeId = peerNodeId;
|
||||
required this._userId,
|
||||
required this._mode,
|
||||
required this._peerNodeId,
|
||||
});
|
||||
|
||||
final DatabaseSession _session;
|
||||
final UuidValue _userId;
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class CrdtSync {
|
|||
int syncBatchSize = defaultSyncBatchSize,
|
||||
|
||||
/// Delay between continuous sync rounds.
|
||||
Duration continuousSyncInterval = defaultContinuousSyncInterval,
|
||||
this._continuousSyncInterval = defaultContinuousSyncInterval,
|
||||
}) : _syncTables = syncTables,
|
||||
_serializationManager = serializationManager,
|
||||
_databaseContext =
|
||||
|
|
@ -55,8 +55,7 @@ class CrdtSync {
|
|||
syncTables: syncTables,
|
||||
serializationManager: serializationManager,
|
||||
),
|
||||
_syncBatchSize = syncBatchSize,
|
||||
_continuousSyncInterval = continuousSyncInterval {
|
||||
_syncBatchSize = syncBatchSize {
|
||||
if (syncBatchSize < 1) {
|
||||
throw ArgumentError.value(syncBatchSize, 'syncBatchSize', 'Must be >= 1');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,14 +67,11 @@ class CrdtDatabase implements Database {
|
|||
CrdtDatabase._(
|
||||
this._delegate,
|
||||
this._context, {
|
||||
required List<Table> syncTables,
|
||||
required int syncBatchSize,
|
||||
required Duration continuousSyncInterval,
|
||||
required this._syncTables,
|
||||
required this._syncBatchSize,
|
||||
required this._continuousSyncInterval,
|
||||
required UuidValue? persistentUserId,
|
||||
}) : _syncTables = syncTables,
|
||||
_syncBatchSize = syncBatchSize,
|
||||
_continuousSyncInterval = continuousSyncInterval,
|
||||
_recorder = CrdtMutationRecorder(
|
||||
}) : _recorder = CrdtMutationRecorder(
|
||||
_delegate,
|
||||
context: _context,
|
||||
persistentUserId: persistentUserId,
|
||||
|
|
|
|||
|
|
@ -56,10 +56,9 @@ class CrdtForeignKeyProjector {
|
|||
/// Creates a projector over the given recorder context.
|
||||
CrdtForeignKeyProjector(
|
||||
this._context, {
|
||||
required CrdtForeignKeyGraph foreignKeys,
|
||||
required CrdtUniqueConflictResolver uniqueResolver,
|
||||
}) : _foreignKeys = foreignKeys,
|
||||
_uniqueResolver = uniqueResolver;
|
||||
required this._foreignKeys,
|
||||
required this._uniqueResolver,
|
||||
});
|
||||
|
||||
final CrdtRecorderContext _context;
|
||||
final CrdtForeignKeyGraph _foreignKeys;
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
// ignore_for_file: dead_code, unnecessary_null_comparison
|
||||
// ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
|
||||
// ignore_for_file: unnecessary_null_comparison
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod_database/serverpod_database.dart' as _isd;
|
||||
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
|
||||
as _icw2tu00;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
|
||||
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
|
||||
|
||||
/// Why a synced user tombstone changed row visibility.
|
||||
|
|
@ -18,8 +19,7 @@ enum CrdtDataDeletedReason implements _iss.SerializableModel {
|
|||
userReinsert,
|
||||
userDelete,
|
||||
userCascadeDelete,
|
||||
uniqueLoser,
|
||||
;
|
||||
uniqueLoser;
|
||||
|
||||
static CrdtDataDeletedReason fromJson(int index) {
|
||||
switch (index) {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
// ignore_for_file: dead_code, unnecessary_null_comparison
|
||||
// ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
|
||||
// ignore_for_file: unnecessary_null_comparison
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod_database/serverpod_database.dart' as _isd;
|
||||
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
|
||||
as _icw2tu00;
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
// ignore_for_file: dead_code, unnecessary_null_comparison
|
||||
// ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
|
||||
// ignore_for_file: unnecessary_null_comparison
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod_database/serverpod_database.dart' as _isd;
|
||||
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
|
||||
as _icw2tu00;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
|
||||
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
|
||||
|
||||
/// Why a foreign key projection override is active.
|
||||
|
|
@ -26,8 +27,7 @@ enum CrdtForeignKeyOverrideReason implements _iss.SerializableModel {
|
|||
/// cannot repair the value (restrict, no action, cascade, or the column is
|
||||
/// non-nullable), so the row is hidden by projection while the attempt is
|
||||
/// preserved.
|
||||
missingParent,
|
||||
;
|
||||
missingParent;
|
||||
|
||||
static CrdtForeignKeyOverrideReason fromJson(int index) {
|
||||
switch (index) {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
// ignore_for_file: type_literal_in_constant_pattern
|
||||
// ignore_for_file: use_super_parameters
|
||||
// ignore_for_file: invalid_use_of_internal_member
|
||||
// ignore_for_file: dead_code, unnecessary_null_comparison
|
||||
// ignore_for_file: dead_code, no_leading_underscores_for_library_prefixes
|
||||
// ignore_for_file: unnecessary_null_comparison
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
import 'package:serverpod_database/serverpod_database.dart' as _isd;
|
||||
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
|
||||
as _icw2tu00;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
|
||||
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
|
||||
|
||||
/// Materialized visibility state for a CRDT row.
|
||||
|
|
@ -22,8 +23,7 @@ enum CrdtDataRowVisibility implements _iss.SerializableModel {
|
|||
foreignKeyRestrictRestore,
|
||||
userDelete,
|
||||
userCascadeDelete,
|
||||
foreignKeyCascade,
|
||||
;
|
||||
foreignKeyCascade;
|
||||
|
||||
static CrdtDataRowVisibility fromJson(int index) {
|
||||
switch (index) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
|
||||
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
|
||||
|
||||
/// Base class for HLC timestamps.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
|
||||
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
|
||||
as _icw2tu00;
|
||||
import 'package:serverpod_serialization/serverpod_serialization.dart' as _iss;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
// ignore_for_file: invalid_use_of_internal_member
|
||||
|
||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||
|
||||
import 'package:serverpod_database/serverpod_database.dart' as _isd;
|
||||
import 'package:serverpod_offline_sync/serverpod_offline_sync.dart'
|
||||
as _icw2tu00;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue