feat: Implement shared scopes with role enforcement (#90)

This commit is contained in:
Marcelo Mendonça Soares 2026-07-03 01:25:15 -03:00 • committed by GitHub
commit 8acfa7f269
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 4868 additions and 485 deletions

View file

@ -94,6 +94,7 @@ abstract class MergeScenarioBenchmark extends AsyncBenchmarkBase {
CrdtMergeInsert insertChangeFor(TableRow<UuidValue?> row) {
final hlc = _nextRemoteHlc();
return CrdtMergeInsert(
uuidScopeId: _userId,
tableName: row.table.tableName,
uuidRowId: row.id!,
uuidNodeId: hlc.nodeId,
@ -110,6 +111,7 @@ abstract class MergeScenarioBenchmark extends AsyncBenchmarkBase {
) {
final hlc = _nextRemoteHlc();
return CrdtMergeUpdate(
uuidScopeId: _userId,
tableName: row.table.tableName,
uuidRowId: row.id!,
uuidNodeId: hlc.nodeId,
@ -123,6 +125,7 @@ abstract class MergeScenarioBenchmark extends AsyncBenchmarkBase {
CrdtMergeDelete deleteChangeFor(TableRow<UuidValue?> row) {
final hlc = _nextRemoteHlc();
return CrdtMergeDelete(
uuidScopeId: _userId,
tableName: row.table.tableName,
uuidRowId: row.id!,
uuidNodeId: hlc.nodeId,

View file

@ -107,12 +107,14 @@ class QueryCountingDatabase implements Database {
List<T> rows, {
Transaction? transaction,
bool ignoreConflicts = false,
bool noReturn = false,
}) async {
queryCount++;
return _delegate.insert<T>(
rows,
transaction: transaction,
ignoreConflicts: ignoreConflicts,
noReturn: noReturn,
);
}
@ -132,6 +134,7 @@ class QueryCountingDatabase implements Database {
List<Column>? updateColumns,
Expression? updateWhere,
Transaction? transaction,
bool noReturn = false,
}) async {
queryCount++;
return _delegate.upsert<T>(
@ -140,6 +143,7 @@ class QueryCountingDatabase implements Database {
updateColumns: updateColumns,
updateWhere: updateWhere,
transaction: transaction,
noReturn: noReturn,
);
}
@ -166,9 +170,15 @@ class QueryCountingDatabase implements Database {
List<T> rows, {
List<Column>? columns,
Transaction? transaction,
bool noReturn = false,
}) async {
queryCount++;
return _delegate.update<T>(rows, columns: columns, transaction: transaction);
return _delegate.update<T>(
rows,
columns: columns,
transaction: transaction,
noReturn: noReturn,
);
}
@override
@ -205,6 +215,7 @@ class QueryCountingDatabase implements Database {
List<Column>? orderByList,
bool orderDescending = false,
Transaction? transaction,
bool noReturn = false,
}) async {
queryCount++;
return _delegate.updateWhere<T>(
@ -218,6 +229,7 @@ class QueryCountingDatabase implements Database {
// ignore: deprecated_member_use
orderDescending: orderDescending,
transaction: transaction,
noReturn: noReturn,
);
}
@ -228,6 +240,7 @@ class QueryCountingDatabase implements Database {
List<Column>? orderByList,
bool orderDescending = false,
Transaction? transaction,
bool noReturn = false,
}) async {
queryCount++;
return _delegate.delete<T>(
@ -238,6 +251,7 @@ class QueryCountingDatabase implements Database {
// ignore: deprecated_member_use
orderDescending: orderDescending,
transaction: transaction,
noReturn: noReturn,
);
}
@ -257,6 +271,7 @@ class QueryCountingDatabase implements Database {
List<Column>? orderByList,
bool orderDescending = false,
Transaction? transaction,
bool noReturn = false,
}) async {
queryCount++;
return _delegate.deleteWhere<T>(
@ -267,6 +282,7 @@ class QueryCountingDatabase implements Database {
// ignore: deprecated_member_use
orderDescending: orderDescending,
transaction: transaction,
noReturn: noReturn,
);
}

View file

@ -142,26 +142,37 @@ WITH RECURSIVE n(i) AS (SELECT 0 UNION ALL SELECT i + 1 FROM n WHERE i < $noiseU
INSERT INTO "crdt_scopes" ("currentNodeId") SELECT NULL FROM n
''');
await db.unsafeExecute('''
INSERT INTO "crdt_nodes" ("scopeId")
SELECT u."id" FROM "crdt_scopes" u
WHERE NOT EXISTS (SELECT 1 FROM "crdt_nodes" n WHERE n."scopeId" = u."id")
WITH local_node(id) AS (
SELECT "currentNodeId" FROM "crdt_scopes" WHERE "id" = $benchUserId
)
INSERT INTO "crdt_scope_nodes" ("scopeId", "nodeId")
SELECT u."id", local_node.id
FROM "crdt_scopes" u
CROSS JOIN local_node
WHERE local_node.id IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM "crdt_scope_nodes" sn
WHERE sn."scopeId" = u."id" AND sn."nodeId" = local_node.id
)
''');
// Noise rows for every noise user, spread across all synced tables, with
// 10% tombstoned. Random UUIDs keep them unrelated to the domain rows.
await db.unsafeExecute('''
WITH RECURSIVE n(i) AS (SELECT 0 UNION ALL SELECT i + 1 FROM n WHERE i < $noiseCrdtRows - 1),
users(rowidx, uid) AS (
SELECT ROW_NUMBER() OVER (ORDER BY "id") - 1, "id"
FROM "crdt_scopes" WHERE "id" <> $benchUserId
users(rowidx, uid, nodeId) AS (
SELECT ROW_NUMBER() OVER (ORDER BY s."id") - 1, s."id", sn."nodeId"
FROM "crdt_scopes" s
JOIN "crdt_scope_nodes" sn ON sn."scopeId" = s."id"
WHERE s."id" <> $benchUserId
),
tbls(tblidx, tid) AS (
SELECT ROW_NUMBER() OVER (ORDER BY "id") - 1, "id" FROM "crdt_schema_tables"
)
INSERT INTO "crdt_data_rows"
("hlcDatetime", "hlcCounter", "scopeId", "tblId", "uuidRowId", "nodeId", "visibility")
SELECT 0, 0, u.uid, t.tid, randomblob(16),
(SELECT nd."id" FROM "crdt_nodes" nd WHERE nd."scopeId" = u.uid LIMIT 1),
SELECT 0, 0, u.uid, t.tid, randomblob(16), u.nodeId,
CASE WHEN n.i % 10 = 0 THEN $hiddenVisibility ELSE 0 END
FROM n
JOIN users u ON u.rowidx = n.i % $noiseUsers