@syncular/server-hono 0.0.6-184 → 0.0.6-188
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +262 -103
- package/dist/routes.js.map +1 -1
- package/package.json +6 -6
- package/src/__tests__/audit-routes.test.ts +23 -17
- package/src/__tests__/create-server.test.ts +16 -12
- package/src/__tests__/pull-chunk-storage.test.ts +17 -13
- package/src/__tests__/sync-maintenance.test.ts +17 -13
- package/src/__tests__/sync-rate-limit-routing.test.ts +17 -13
- package/src/routes.ts +373 -131
package/src/routes.ts
CHANGED
|
@@ -17,7 +17,9 @@ import {
|
|
|
17
17
|
ScopeValuesSchema,
|
|
18
18
|
SyncCombinedRequestSchema,
|
|
19
19
|
SyncCombinedResponseSchema,
|
|
20
|
+
type SyncPushCommitRequestSchema,
|
|
20
21
|
SyncPushRequestSchema,
|
|
22
|
+
type SyncPushResponse,
|
|
21
23
|
} from '@syncular/core';
|
|
22
24
|
import type {
|
|
23
25
|
ScopeCacheBackend,
|
|
@@ -42,6 +44,7 @@ import {
|
|
|
42
44
|
parseJsonValue,
|
|
43
45
|
pull,
|
|
44
46
|
pushCommit,
|
|
47
|
+
pushCommitBatch,
|
|
45
48
|
readSnapshotChunk,
|
|
46
49
|
recordClientCursor,
|
|
47
50
|
resolveEffectiveScopesForSubscriptions,
|
|
@@ -880,8 +883,8 @@ export function createSyncRoutes<
|
|
|
880
883
|
};
|
|
881
884
|
|
|
882
885
|
type PushRequestBody = Omit<
|
|
883
|
-
z.infer<typeof
|
|
884
|
-
|
|
886
|
+
z.infer<typeof SyncPushCommitRequestSchema>,
|
|
887
|
+
never
|
|
885
888
|
>;
|
|
886
889
|
|
|
887
890
|
type PushExecutionContext = {
|
|
@@ -894,11 +897,291 @@ export function createSyncRoutes<
|
|
|
894
897
|
syncPath: 'http-combined' | 'ws-push';
|
|
895
898
|
};
|
|
896
899
|
|
|
900
|
+
type ExecutedPushCommit = Awaited<ReturnType<typeof pushCommit>>;
|
|
901
|
+
|
|
902
|
+
type PushExecutionSummary = {
|
|
903
|
+
durationMs: number;
|
|
904
|
+
outcome: string;
|
|
905
|
+
commitSeq: number | null;
|
|
906
|
+
operationCount: number;
|
|
907
|
+
tables: string[];
|
|
908
|
+
results: SyncPushResponse['results'];
|
|
909
|
+
payloadSnapshot: RequestPayloadSnapshot | null;
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
function notifyRealtimeForAppliedPushes(
|
|
913
|
+
ctx: PushExecutionContext,
|
|
914
|
+
pushedCommits: ExecutedPushCommit[]
|
|
915
|
+
): void {
|
|
916
|
+
if (!wsConnectionManager && !realtimeBroadcaster) {
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
let latestCommitSeq = 0;
|
|
921
|
+
let latestActorId = ctx.auth.actorId;
|
|
922
|
+
let latestCreatedAt: string | undefined;
|
|
923
|
+
const scopeKeys = new Set<string>();
|
|
924
|
+
const emittedChanges: ExecutedPushCommit['emittedChanges'] = [];
|
|
925
|
+
|
|
926
|
+
for (const pushed of pushedCommits) {
|
|
927
|
+
if (
|
|
928
|
+
pushed.response.ok !== true ||
|
|
929
|
+
pushed.response.status !== 'applied' ||
|
|
930
|
+
typeof pushed.response.commitSeq !== 'number'
|
|
931
|
+
) {
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
latestCommitSeq = Math.max(latestCommitSeq, pushed.response.commitSeq);
|
|
936
|
+
latestActorId = pushed.commitActorId ?? latestActorId;
|
|
937
|
+
latestCreatedAt = pushed.commitCreatedAt ?? latestCreatedAt;
|
|
938
|
+
for (const scopeKey of applyPartitionToScopeKeys(
|
|
939
|
+
ctx.partitionId,
|
|
940
|
+
pushed.scopeKeys
|
|
941
|
+
)) {
|
|
942
|
+
scopeKeys.add(scopeKey);
|
|
943
|
+
}
|
|
944
|
+
emittedChanges.push(...pushed.emittedChanges);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
if (latestCommitSeq <= 0 || scopeKeys.size === 0) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const combinedScopeKeys = Array.from(scopeKeys);
|
|
952
|
+
|
|
953
|
+
if (wsConnectionManager) {
|
|
954
|
+
wsConnectionManager.notifyScopeKeys(combinedScopeKeys, latestCommitSeq, {
|
|
955
|
+
excludeClientIds: [ctx.clientId],
|
|
956
|
+
changes: emittedChanges,
|
|
957
|
+
actorId: latestActorId,
|
|
958
|
+
createdAt: latestCreatedAt,
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if (realtimeBroadcaster) {
|
|
963
|
+
realtimeBroadcaster
|
|
964
|
+
.publish({
|
|
965
|
+
type: 'commit',
|
|
966
|
+
commitSeq: latestCommitSeq,
|
|
967
|
+
partitionId: ctx.partitionId,
|
|
968
|
+
scopeKeys: combinedScopeKeys,
|
|
969
|
+
sourceInstanceId: instanceId,
|
|
970
|
+
})
|
|
971
|
+
.catch((error) => {
|
|
972
|
+
logAsyncFailureOnce('sync.realtime.broadcast_publish_failed', {
|
|
973
|
+
event: 'sync.realtime.broadcast_publish_failed',
|
|
974
|
+
userId: ctx.auth.actorId,
|
|
975
|
+
clientId: ctx.clientId,
|
|
976
|
+
error: error instanceof Error ? error.message : String(error),
|
|
977
|
+
});
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function recordPushExecutionSideEffects(
|
|
983
|
+
ctx: PushExecutionContext,
|
|
984
|
+
summary: PushExecutionSummary
|
|
985
|
+
): void {
|
|
986
|
+
recordRequestEventInBackground(() => ({
|
|
987
|
+
partitionId: ctx.partitionId,
|
|
988
|
+
requestId: ctx.requestId,
|
|
989
|
+
traceId: ctx.traceContext.traceId,
|
|
990
|
+
spanId: ctx.traceContext.spanId,
|
|
991
|
+
eventType: 'push',
|
|
992
|
+
syncPath: ctx.syncPath,
|
|
993
|
+
actorId: ctx.auth.actorId,
|
|
994
|
+
clientId: ctx.clientId,
|
|
995
|
+
transportPath: ctx.transportPath,
|
|
996
|
+
statusCode: 200,
|
|
997
|
+
outcome: summary.outcome,
|
|
998
|
+
responseStatus: normalizeResponseStatus(200, summary.outcome),
|
|
999
|
+
durationMs: summary.durationMs,
|
|
1000
|
+
errorCode: firstPushErrorCode(summary.results),
|
|
1001
|
+
commitSeq: summary.commitSeq,
|
|
1002
|
+
operationCount: summary.operationCount,
|
|
1003
|
+
tables: summary.tables,
|
|
1004
|
+
payloadSnapshot: summary.payloadSnapshot,
|
|
1005
|
+
}));
|
|
1006
|
+
|
|
1007
|
+
emitConsoleLiveEvent(consoleLiveEmitter, 'push', () => ({
|
|
1008
|
+
partitionId: ctx.partitionId,
|
|
1009
|
+
requestId: ctx.requestId,
|
|
1010
|
+
traceId: ctx.traceContext.traceId,
|
|
1011
|
+
spanId: ctx.traceContext.spanId,
|
|
1012
|
+
actorId: ctx.auth.actorId,
|
|
1013
|
+
clientId: ctx.clientId,
|
|
1014
|
+
transportPath: ctx.transportPath,
|
|
1015
|
+
syncPath: ctx.syncPath,
|
|
1016
|
+
outcome: summary.outcome,
|
|
1017
|
+
statusCode: 200,
|
|
1018
|
+
durationMs: summary.durationMs,
|
|
1019
|
+
commitSeq: summary.commitSeq,
|
|
1020
|
+
operationCount: summary.operationCount,
|
|
1021
|
+
tables: summary.tables,
|
|
1022
|
+
}));
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function maybeCountPushConflicts(
|
|
1026
|
+
ctx: PushExecutionContext,
|
|
1027
|
+
results: SyncPushResponse['results'],
|
|
1028
|
+
enabled?: boolean
|
|
1029
|
+
): void {
|
|
1030
|
+
if (enabled !== true) {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
const detectedConflicts = results.reduce(
|
|
1035
|
+
(count, result) => count + (result.status === 'conflict' ? 1 : 0),
|
|
1036
|
+
0
|
|
1037
|
+
);
|
|
1038
|
+
if (detectedConflicts <= 0) {
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
countSyncMetric('sync.conflicts.detected', detectedConflicts, {
|
|
1043
|
+
attributes: {
|
|
1044
|
+
syncPath: ctx.syncPath,
|
|
1045
|
+
transportPath: ctx.transportPath,
|
|
1046
|
+
},
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
function emitCommitLiveEvents(
|
|
1051
|
+
ctx: PushExecutionContext,
|
|
1052
|
+
pushedCommits: ExecutedPushCommit[]
|
|
1053
|
+
): void {
|
|
1054
|
+
for (const pushed of pushedCommits) {
|
|
1055
|
+
if (
|
|
1056
|
+
pushed.response.ok !== true ||
|
|
1057
|
+
pushed.response.status !== 'applied' ||
|
|
1058
|
+
typeof pushed.response.commitSeq !== 'number'
|
|
1059
|
+
) {
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
emitConsoleLiveEvent(consoleLiveEmitter, 'commit', () => ({
|
|
1064
|
+
partitionId: ctx.partitionId,
|
|
1065
|
+
commitSeq: pushed.response.commitSeq,
|
|
1066
|
+
actorId: ctx.auth.actorId,
|
|
1067
|
+
clientId: ctx.clientId,
|
|
1068
|
+
affectedTables: pushed.affectedTables,
|
|
1069
|
+
}));
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
async function executePushCommitBatchWithSideEffects(
|
|
1074
|
+
ctx: PushExecutionContext,
|
|
1075
|
+
pushBodies: PushRequestBody[],
|
|
1076
|
+
execOptions: {
|
|
1077
|
+
countConflictsMetric?: boolean;
|
|
1078
|
+
} = {}
|
|
1079
|
+
): Promise<ExecutedPushCommit[]> {
|
|
1080
|
+
const timer = createSyncTimer();
|
|
1081
|
+
const totalOperationCount = pushBodies.reduce(
|
|
1082
|
+
(count, pushBody) => count + (pushBody.operations?.length ?? 0),
|
|
1083
|
+
0
|
|
1084
|
+
);
|
|
1085
|
+
const executedPushes = await pushCommitBatch({
|
|
1086
|
+
db: options.db,
|
|
1087
|
+
dialect: options.dialect,
|
|
1088
|
+
handlers: handlerRegistry,
|
|
1089
|
+
plugins: options.plugins,
|
|
1090
|
+
auth: ctx.auth,
|
|
1091
|
+
suppressTelemetry: true,
|
|
1092
|
+
requests: pushBodies.map((pushBody) => ({
|
|
1093
|
+
clientId: ctx.clientId,
|
|
1094
|
+
clientCommitId: pushBody.clientCommitId,
|
|
1095
|
+
operations: pushBody.operations,
|
|
1096
|
+
schemaVersion: pushBody.schemaVersion,
|
|
1097
|
+
})),
|
|
1098
|
+
});
|
|
1099
|
+
const affectedTables = new Set<string>();
|
|
1100
|
+
for (const pushed of executedPushes) {
|
|
1101
|
+
for (const table of pushed.affectedTables) {
|
|
1102
|
+
affectedTables.add(table);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
const pushDurationMs = timer();
|
|
1107
|
+
const latestCommitSeq = executedPushes.reduce((latest, pushed) => {
|
|
1108
|
+
if (typeof pushed.response.commitSeq === 'number') {
|
|
1109
|
+
return Math.max(latest, pushed.response.commitSeq);
|
|
1110
|
+
}
|
|
1111
|
+
return latest;
|
|
1112
|
+
}, 0);
|
|
1113
|
+
const aggregateStatus = executedPushes.every(
|
|
1114
|
+
(pushed) => pushed.response.status === 'cached'
|
|
1115
|
+
)
|
|
1116
|
+
? 'cached'
|
|
1117
|
+
: executedPushes.every(
|
|
1118
|
+
(pushed) =>
|
|
1119
|
+
pushed.response.status === 'applied' ||
|
|
1120
|
+
pushed.response.status === 'cached'
|
|
1121
|
+
)
|
|
1122
|
+
? 'applied'
|
|
1123
|
+
: 'rejected';
|
|
1124
|
+
const aggregatedResults = executedPushes.flatMap(
|
|
1125
|
+
(pushed) => pushed.response.results
|
|
1126
|
+
);
|
|
1127
|
+
|
|
1128
|
+
logSyncEvent({
|
|
1129
|
+
event: 'sync.push',
|
|
1130
|
+
userId: ctx.auth.actorId,
|
|
1131
|
+
durationMs: pushDurationMs,
|
|
1132
|
+
operationCount: totalOperationCount,
|
|
1133
|
+
status: aggregateStatus,
|
|
1134
|
+
commitSeq: latestCommitSeq > 0 ? latestCommitSeq : undefined,
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
recordPushExecutionSideEffects(ctx, {
|
|
1138
|
+
durationMs: pushDurationMs,
|
|
1139
|
+
outcome: aggregateStatus,
|
|
1140
|
+
commitSeq: latestCommitSeq > 0 ? latestCommitSeq : null,
|
|
1141
|
+
operationCount: totalOperationCount,
|
|
1142
|
+
tables: Array.from(affectedTables),
|
|
1143
|
+
results: aggregatedResults,
|
|
1144
|
+
payloadSnapshot: shouldCaptureRequestPayloadSnapshots
|
|
1145
|
+
? {
|
|
1146
|
+
request: {
|
|
1147
|
+
clientId: ctx.clientId,
|
|
1148
|
+
commits: pushBodies.map((pushBody) => ({
|
|
1149
|
+
clientCommitId: pushBody.clientCommitId,
|
|
1150
|
+
schemaVersion: pushBody.schemaVersion,
|
|
1151
|
+
operations: pushBody.operations,
|
|
1152
|
+
})),
|
|
1153
|
+
},
|
|
1154
|
+
response: {
|
|
1155
|
+
ok: true,
|
|
1156
|
+
commits: executedPushes.map((pushed, index) => ({
|
|
1157
|
+
clientCommitId: pushBodies[index]?.clientCommitId ?? '',
|
|
1158
|
+
...pushed.response,
|
|
1159
|
+
})),
|
|
1160
|
+
},
|
|
1161
|
+
}
|
|
1162
|
+
: null,
|
|
1163
|
+
});
|
|
1164
|
+
|
|
1165
|
+
maybeCountPushConflicts(
|
|
1166
|
+
ctx,
|
|
1167
|
+
aggregatedResults,
|
|
1168
|
+
execOptions.countConflictsMetric
|
|
1169
|
+
);
|
|
1170
|
+
|
|
1171
|
+
notifyRealtimeForAppliedPushes(ctx, executedPushes);
|
|
1172
|
+
emitCommitLiveEvents(ctx, executedPushes);
|
|
1173
|
+
|
|
1174
|
+
return executedPushes;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
897
1177
|
async function executePushCommitWithSideEffects(
|
|
898
1178
|
ctx: PushExecutionContext,
|
|
899
1179
|
pushBody: PushRequestBody,
|
|
900
|
-
execOptions: {
|
|
901
|
-
|
|
1180
|
+
execOptions: {
|
|
1181
|
+
countConflictsMetric?: boolean;
|
|
1182
|
+
deferRealtimeNotifications?: boolean;
|
|
1183
|
+
} = {}
|
|
1184
|
+
): Promise<ExecutedPushCommit> {
|
|
902
1185
|
const timer = createSyncTimer();
|
|
903
1186
|
const pushOps = pushBody.operations ?? [];
|
|
904
1187
|
|
|
@@ -927,24 +1210,13 @@ export function createSyncRoutes<
|
|
|
927
1210
|
commitSeq: pushed.response.commitSeq,
|
|
928
1211
|
});
|
|
929
1212
|
|
|
930
|
-
|
|
931
|
-
partitionId: ctx.partitionId,
|
|
932
|
-
requestId: ctx.requestId,
|
|
933
|
-
traceId: ctx.traceContext.traceId,
|
|
934
|
-
spanId: ctx.traceContext.spanId,
|
|
935
|
-
eventType: 'push',
|
|
936
|
-
syncPath: ctx.syncPath,
|
|
937
|
-
actorId: ctx.auth.actorId,
|
|
938
|
-
clientId: ctx.clientId,
|
|
939
|
-
transportPath: ctx.transportPath,
|
|
940
|
-
statusCode: 200,
|
|
941
|
-
outcome: pushed.response.status,
|
|
942
|
-
responseStatus: normalizeResponseStatus(200, pushed.response.status),
|
|
1213
|
+
recordPushExecutionSideEffects(ctx, {
|
|
943
1214
|
durationMs: pushDurationMs,
|
|
944
|
-
|
|
945
|
-
commitSeq: pushed.response.commitSeq,
|
|
1215
|
+
outcome: pushed.response.status,
|
|
1216
|
+
commitSeq: pushed.response.commitSeq ?? null,
|
|
946
1217
|
operationCount: pushOps.length,
|
|
947
1218
|
tables: pushed.affectedTables,
|
|
1219
|
+
results: pushed.response.results,
|
|
948
1220
|
payloadSnapshot: shouldCaptureRequestPayloadSnapshots
|
|
949
1221
|
? {
|
|
950
1222
|
request: {
|
|
@@ -956,97 +1228,18 @@ export function createSyncRoutes<
|
|
|
956
1228
|
response: pushed.response,
|
|
957
1229
|
}
|
|
958
1230
|
: null,
|
|
959
|
-
})
|
|
960
|
-
|
|
961
|
-
emitConsoleLiveEvent(consoleLiveEmitter, 'push', () => ({
|
|
962
|
-
partitionId: ctx.partitionId,
|
|
963
|
-
requestId: ctx.requestId,
|
|
964
|
-
traceId: ctx.traceContext.traceId,
|
|
965
|
-
spanId: ctx.traceContext.spanId,
|
|
966
|
-
actorId: ctx.auth.actorId,
|
|
967
|
-
clientId: ctx.clientId,
|
|
968
|
-
transportPath: ctx.transportPath,
|
|
969
|
-
syncPath: ctx.syncPath,
|
|
970
|
-
outcome: pushed.response.status,
|
|
971
|
-
statusCode: 200,
|
|
972
|
-
durationMs: pushDurationMs,
|
|
973
|
-
commitSeq: pushed.response.commitSeq ?? null,
|
|
974
|
-
operationCount: pushOps.length,
|
|
975
|
-
tables: pushed.affectedTables,
|
|
976
|
-
}));
|
|
977
|
-
|
|
978
|
-
if (execOptions.countConflictsMetric === true) {
|
|
979
|
-
const detectedConflicts = pushed.response.results.reduce(
|
|
980
|
-
(count, result) => count + (result.status === 'conflict' ? 1 : 0),
|
|
981
|
-
0
|
|
982
|
-
);
|
|
983
|
-
if (detectedConflicts > 0) {
|
|
984
|
-
countSyncMetric('sync.conflicts.detected', detectedConflicts, {
|
|
985
|
-
attributes: {
|
|
986
|
-
syncPath: ctx.syncPath,
|
|
987
|
-
transportPath: ctx.transportPath,
|
|
988
|
-
},
|
|
989
|
-
});
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
if (
|
|
994
|
-
wsConnectionManager &&
|
|
995
|
-
pushed.response.ok === true &&
|
|
996
|
-
pushed.response.status === 'applied' &&
|
|
997
|
-
typeof pushed.response.commitSeq === 'number'
|
|
998
|
-
) {
|
|
999
|
-
const scopeKeys = applyPartitionToScopeKeys(
|
|
1000
|
-
ctx.partitionId,
|
|
1001
|
-
pushed.scopeKeys
|
|
1002
|
-
);
|
|
1003
|
-
|
|
1004
|
-
if (scopeKeys.length > 0) {
|
|
1005
|
-
wsConnectionManager.notifyScopeKeys(
|
|
1006
|
-
scopeKeys,
|
|
1007
|
-
pushed.response.commitSeq,
|
|
1008
|
-
{
|
|
1009
|
-
excludeClientIds: [ctx.clientId],
|
|
1010
|
-
changes: pushed.emittedChanges,
|
|
1011
|
-
actorId: pushed.commitActorId ?? ctx.auth.actorId,
|
|
1012
|
-
createdAt: pushed.commitCreatedAt ?? new Date().toISOString(),
|
|
1013
|
-
}
|
|
1014
|
-
);
|
|
1231
|
+
});
|
|
1015
1232
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
partitionId: ctx.partitionId,
|
|
1022
|
-
scopeKeys,
|
|
1023
|
-
sourceInstanceId: instanceId,
|
|
1024
|
-
})
|
|
1025
|
-
.catch((error) => {
|
|
1026
|
-
logAsyncFailureOnce('sync.realtime.broadcast_publish_failed', {
|
|
1027
|
-
event: 'sync.realtime.broadcast_publish_failed',
|
|
1028
|
-
userId: ctx.auth.actorId,
|
|
1029
|
-
clientId: ctx.clientId,
|
|
1030
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1031
|
-
});
|
|
1032
|
-
});
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1233
|
+
maybeCountPushConflicts(
|
|
1234
|
+
ctx,
|
|
1235
|
+
pushed.response.results,
|
|
1236
|
+
execOptions.countConflictsMetric
|
|
1237
|
+
);
|
|
1036
1238
|
|
|
1037
|
-
if (
|
|
1038
|
-
pushed
|
|
1039
|
-
pushed.response.status === 'applied' &&
|
|
1040
|
-
typeof pushed.response.commitSeq === 'number'
|
|
1041
|
-
) {
|
|
1042
|
-
emitConsoleLiveEvent(consoleLiveEmitter, 'commit', () => ({
|
|
1043
|
-
partitionId: ctx.partitionId,
|
|
1044
|
-
commitSeq: pushed.response.commitSeq,
|
|
1045
|
-
actorId: ctx.auth.actorId,
|
|
1046
|
-
clientId: ctx.clientId,
|
|
1047
|
-
affectedTables: pushed.affectedTables,
|
|
1048
|
-
}));
|
|
1239
|
+
if (execOptions.deferRealtimeNotifications !== true) {
|
|
1240
|
+
notifyRealtimeForAppliedPushes(ctx, [pushed]);
|
|
1049
1241
|
}
|
|
1242
|
+
emitCommitLiveEvents(ctx, [pushed]);
|
|
1050
1243
|
|
|
1051
1244
|
return pushed;
|
|
1052
1245
|
}
|
|
@@ -1465,36 +1658,85 @@ export function createSyncRoutes<
|
|
|
1465
1658
|
|
|
1466
1659
|
let pushResponse:
|
|
1467
1660
|
| undefined
|
|
1468
|
-
|
|
|
1661
|
+
| {
|
|
1662
|
+
ok: true;
|
|
1663
|
+
commits: Array<
|
|
1664
|
+
Awaited<ReturnType<typeof pushCommit>>['response'] & {
|
|
1665
|
+
clientCommitId: string;
|
|
1666
|
+
}
|
|
1667
|
+
>;
|
|
1668
|
+
};
|
|
1469
1669
|
let pullResponse: undefined | PullResult['response'];
|
|
1470
1670
|
|
|
1471
1671
|
// --- Push phase ---
|
|
1472
1672
|
if (body.push) {
|
|
1473
|
-
const
|
|
1474
|
-
const
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1673
|
+
const pushBodies = body.push.commits ?? [];
|
|
1674
|
+
const pushedCommits: NonNullable<typeof pushResponse>['commits'] = [];
|
|
1675
|
+
for (const pushBody of pushBodies) {
|
|
1676
|
+
const pushOps = pushBody.operations ?? [];
|
|
1677
|
+
if (pushOps.length > maxOperationsPerPush) {
|
|
1678
|
+
return c.json(
|
|
1679
|
+
{
|
|
1680
|
+
error: 'TOO_MANY_OPERATIONS',
|
|
1681
|
+
message: `Maximum ${maxOperationsPerPush} operations per push`,
|
|
1682
|
+
},
|
|
1683
|
+
400
|
|
1684
|
+
);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
const executedPushes =
|
|
1688
|
+
pushBodies.length > 1
|
|
1689
|
+
? await executePushCommitBatchWithSideEffects(
|
|
1690
|
+
{
|
|
1691
|
+
auth,
|
|
1692
|
+
clientId,
|
|
1693
|
+
partitionId,
|
|
1694
|
+
requestId,
|
|
1695
|
+
traceContext,
|
|
1696
|
+
transportPath,
|
|
1697
|
+
syncPath: 'http-combined',
|
|
1698
|
+
},
|
|
1699
|
+
pushBodies,
|
|
1700
|
+
{
|
|
1701
|
+
countConflictsMetric: true,
|
|
1702
|
+
}
|
|
1703
|
+
)
|
|
1704
|
+
: [];
|
|
1705
|
+
|
|
1706
|
+
for (let index = 0; index < pushBodies.length; index += 1) {
|
|
1707
|
+
const pushBody = pushBodies[index];
|
|
1708
|
+
if (!pushBody) continue;
|
|
1709
|
+
const pushed =
|
|
1710
|
+
pushBodies.length > 1
|
|
1711
|
+
? executedPushes[index]
|
|
1712
|
+
: await executePushCommitWithSideEffects(
|
|
1713
|
+
{
|
|
1714
|
+
auth,
|
|
1715
|
+
clientId,
|
|
1716
|
+
partitionId,
|
|
1717
|
+
requestId,
|
|
1718
|
+
traceContext,
|
|
1719
|
+
transportPath,
|
|
1720
|
+
syncPath: 'http-combined',
|
|
1721
|
+
},
|
|
1722
|
+
pushBody,
|
|
1723
|
+
{
|
|
1724
|
+
countConflictsMetric: true,
|
|
1725
|
+
}
|
|
1726
|
+
);
|
|
1727
|
+
if (!pushed) {
|
|
1728
|
+
throw new Error('Server returned incomplete batched push result');
|
|
1729
|
+
}
|
|
1730
|
+
pushedCommits.push({
|
|
1731
|
+
clientCommitId: pushBody.clientCommitId,
|
|
1732
|
+
...pushed.response,
|
|
1733
|
+
});
|
|
1483
1734
|
}
|
|
1484
|
-
const pushed = await executePushCommitWithSideEffects(
|
|
1485
|
-
{
|
|
1486
|
-
auth,
|
|
1487
|
-
clientId,
|
|
1488
|
-
partitionId,
|
|
1489
|
-
requestId,
|
|
1490
|
-
traceContext,
|
|
1491
|
-
transportPath,
|
|
1492
|
-
syncPath: 'http-combined',
|
|
1493
|
-
},
|
|
1494
|
-
pushBody
|
|
1495
|
-
);
|
|
1496
1735
|
|
|
1497
|
-
pushResponse =
|
|
1736
|
+
pushResponse = {
|
|
1737
|
+
ok: true,
|
|
1738
|
+
commits: pushedCommits,
|
|
1739
|
+
};
|
|
1498
1740
|
}
|
|
1499
1741
|
|
|
1500
1742
|
// --- Pull phase ---
|