hikoutei 0.4.2 → 0.4.5
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/README.md +20 -0
- package/dist/adapter/persistence/providers/mikro-orm/observation/MikroOrmMappedObservation.d.ts +2 -2
- package/dist/adapter/persistence/providers/mikro-orm/observation/MikroOrmMappedObservation.js +2 -2
- package/dist/adapter/persistence/providers/mikro-orm/observation/MikroOrmUserInputPolling.js +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/observation/MikroOrmUserInputPolling.js.map +1 -1
- package/dist/api/Hikoutei.d.ts +56 -10
- package/dist/api/Hikoutei.d.ts.map +1 -1
- package/dist/api/Hikoutei.js +84 -13
- package/dist/api/Hikoutei.js.map +1 -1
- package/dist/api/entity.d.ts +17 -1
- package/dist/api/entity.d.ts.map +1 -1
- package/dist/api/entity.js +34 -2
- package/dist/api/entity.js.map +1 -1
- package/dist/api/errors.d.ts +24 -0
- package/dist/api/errors.d.ts.map +1 -1
- package/dist/api/errors.js +24 -0
- package/dist/api/errors.js.map +1 -1
- package/dist/application/orm/api/contracts.d.ts +1 -1
- package/dist/application/orm/api/contracts.d.ts.map +1 -1
- package/dist/application/orm/persistence/flush/flushCoordinator.d.ts.map +1 -1
- package/dist/application/orm/persistence/flush/flushCoordinator.js +2 -0
- package/dist/application/orm/persistence/flush/flushCoordinator.js.map +1 -1
- package/dist/application/sync/inbound/autoSystemConflictResolution.d.ts.map +1 -1
- package/dist/application/sync/inbound/autoSystemConflictResolution.js +3 -0
- package/dist/application/sync/inbound/autoSystemConflictResolution.js.map +1 -1
- package/dist/application/sync/outbound/SheetsEffectDispatcher.d.ts +64 -0
- package/dist/application/sync/outbound/SheetsEffectDispatcher.d.ts.map +1 -0
- package/dist/application/sync/outbound/SheetsEffectDispatcher.js +440 -0
- package/dist/application/sync/outbound/SheetsEffectDispatcher.js.map +1 -0
- package/dist/application/sync/service/SyncServiceBootstrap.d.ts +3 -4
- package/dist/application/sync/service/SyncServiceBootstrap.d.ts.map +1 -1
- package/dist/application/sync/service/SyncServiceBootstrap.js +91 -6
- package/dist/application/sync/service/SyncServiceBootstrap.js.map +1 -1
- package/dist/application/sync/service/syncAutoStart.d.ts +127 -0
- package/dist/application/sync/service/syncAutoStart.d.ts.map +1 -0
- package/dist/application/sync/service/syncAutoStart.js +280 -0
- package/dist/application/sync/service/syncAutoStart.js.map +1 -0
- package/dist/application/sync/sheets/conflictProjectionRegistration.d.ts.map +1 -1
- package/dist/application/sync/sheets/conflictProjectionRegistration.js +1 -0
- package/dist/application/sync/sheets/conflictProjectionRegistration.js.map +1 -1
- package/dist/application/sync/telemetry/syncTiming.d.ts +20 -33
- package/dist/application/sync/telemetry/syncTiming.d.ts.map +1 -1
- package/dist/application/sync/telemetry/syncTiming.js +14 -12
- package/dist/application/sync/telemetry/syncTiming.js.map +1 -1
- package/dist/infrastructure/storage/errors.d.ts +10 -5
- package/dist/infrastructure/storage/errors.d.ts.map +1 -1
- package/dist/infrastructure/storage/errors.js +10 -7
- package/dist/infrastructure/storage/errors.js.map +1 -1
- package/dist/infrastructure/storage/index.d.ts +6 -4
- package/dist/infrastructure/storage/index.d.ts.map +1 -1
- package/dist/infrastructure/storage/index.js +3 -2
- package/dist/infrastructure/storage/index.js.map +1 -1
- package/dist/infrastructure/storage/sqlite/schema.d.ts +12 -11
- package/dist/infrastructure/storage/sqlite/schema.d.ts.map +1 -1
- package/dist/infrastructure/storage/sqlite/schema.js +17 -109
- package/dist/infrastructure/storage/sqlite/schema.js.map +1 -1
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts +2 -2
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.js +2 -2
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.js.map +1 -1
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.d.ts +1 -2
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.js +2 -2
- package/dist/infrastructure/storage/state/mapped/mappedPersistenceContext.js.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.js +1 -1
- package/dist/infrastructure/storage/state/observation/observationQuarantine.js.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationTypes.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationTypes.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationValidation.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationValidation.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationWriter.d.ts +1 -1
- package/dist/infrastructure/storage/state/observation/observationWriter.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/observation/observationWriter.js +1 -2
- package/dist/infrastructure/storage/state/observation/observationWriter.js.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts +1 -2
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.js +1 -2
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.js.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts.map +1 -1
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js +2 -2
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js.map +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.d.ts +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.d.ts.map +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.js +1 -1
- package/dist/infrastructure/storage/sync/shared/spreadsheetAuthority.js.map +1 -1
- package/dist/infrastructure/storage/sync/shared/syncRegistry.d.ts +1 -1
- package/dist/infrastructure/storage/sync/shared/syncRegistry.js +1 -1
- package/package.json +13 -7
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.d.ts +0 -66
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.js +0 -123
- package/dist/application/sync/outbound/effects/AdaptiveEffectBatchController.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts +0 -111
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js +0 -369
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts +0 -127
- package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorker.js +0 -552
- package/dist/application/sync/outbound/effects/SyncEffectWorker.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts +0 -85
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js +0 -74
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts +0 -31
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js +0 -222
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts +0 -14
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js +0 -25
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts +0 -61
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js +0 -296
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts +0 -17
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js +0 -80
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts +0 -13
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts.map +0 -1
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js +0 -248
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts +0 -141
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.js +0 -318
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts +0 -143
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js +0 -16
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts +0 -33
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js +0 -259
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts +0 -27
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js +0 -316
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js.map +0 -1
- package/dist/infrastructure/storage/sync/shared/writerLease.d.ts +0 -76
- package/dist/infrastructure/storage/sync/shared/writerLease.d.ts.map +0 -1
- package/dist/infrastructure/storage/sync/shared/writerLease.js +0 -189
- package/dist/infrastructure/storage/sync/shared/writerLease.js.map +0 -1
- package/docs/advanced-sheets-gateway-concurrency-problem.md +0 -434
- package/docs/architecture.md +0 -224
- package/docs/ci.md +0 -293
- package/docs/code-guidelines.md +0 -248
- package/docs/development.md +0 -74
- package/docs/gateway-removal-inventory.md +0 -147
- package/docs/git-workflow.md +0 -224
- package/docs/google-sheets-sync-scaling-strategy.md +0 -459
- package/docs/mikro-orm-adapter-spike.md +0 -89
- package/docs/quick-start.md +0 -137
- package/docs/sql-layer-plan.md +0 -59
- package/docs/sync-bulk-write-benchmark.md +0 -2117
- package/docs/sync-observability.md +0 -100
- package/docs/task-queue-write-model.md +0 -640
- package/docs/typed-sheets-mvp-scope-2026-06-29.md +0 -490
- package/docs/typed-sheets-plan.md +0 -417
- package/docs/write-and-synchronization-flow.md +0 -179
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sheets dispatcher: the host implementation of the package worker's
|
|
3
|
+
* `Dispatcher` boundary over the Google Sheets sync provider.
|
|
4
|
+
*
|
|
5
|
+
* The worker owns selection, claiming, grouping, lease refresh, transitions,
|
|
6
|
+
* and recovery. This dispatcher owns every payload-derived decision: route
|
|
7
|
+
* keys, fast-append candidacy, payload validation, the User_Input candidate
|
|
8
|
+
* gate (via the active-candidate guard SQL), remote evidence validation
|
|
9
|
+
* against effect targets, and transport-outcome classification.
|
|
10
|
+
*/
|
|
11
|
+
import { NON_NEGATIVE_SAFE_INTEGER_MINIMUM, } from "../../../domain/index.js";
|
|
12
|
+
import { DispatchTransportError, } from "@hikoutei/outbox";
|
|
13
|
+
import { CONFLICT_STATUSES, EFFECT_KINDS, EFFECT_TARGET_KINDS, } from "../../../domain/model/constants.js";
|
|
14
|
+
import { absentValue, presentValue, } from "../../../shared/state/index.js";
|
|
15
|
+
import { stableHash } from "../../../shared/encoding/stableEncode.js";
|
|
16
|
+
import { hasActiveUserInputCandidateWithSql, } from "../../../infrastructure/storage/sync/outbound/effectWorkerSql.js";
|
|
17
|
+
import { ensureSpreadsheetAuthorityWithAdapter, } from "../../../infrastructure/storage/sync/shared/spreadsheetAuthority.js";
|
|
18
|
+
import { parseSyncProjectionEffectPayload, } from "../sheets/syncSheets.js";
|
|
19
|
+
import { SYNC_EFFECT_RESULT_STATUSES, SYNC_FAST_APPEND_STATUSES, SYNC_POSTCONDITION_DISPOSITIONS, SYNC_POSTCONDITION_MODES, SYNC_POSTCONDITION_STATUSES, SYNC_PROJECTIONS, } from "../sheets/constants.js";
|
|
20
|
+
import { classifyTransportOutcome, TRANSPORT_OUTCOME_KINDS, } from "../sheets/transportOutcome.js";
|
|
21
|
+
import { fromSqlNullable } from "../../../infrastructure/storage/sqlite/sqlState.js";
|
|
22
|
+
/**
|
|
23
|
+
* Builds the stable grouping key shared by all provider operations on one
|
|
24
|
+
* route. The route includes payload-derived tab/range/schema fields, so one
|
|
25
|
+
* dispatched group always maps to exactly one provider request.
|
|
26
|
+
*/
|
|
27
|
+
export function sheetsRouteKeyFor(effect) {
|
|
28
|
+
const payload = parseSyncProjectionEffectPayload(effect.payload_json);
|
|
29
|
+
return [
|
|
30
|
+
effect.physical_sheet_id,
|
|
31
|
+
payload.sheetName,
|
|
32
|
+
payload.registeredRange,
|
|
33
|
+
effect.projection,
|
|
34
|
+
payload.schemaVersion,
|
|
35
|
+
].join("\u0000");
|
|
36
|
+
}
|
|
37
|
+
/** Validates the opaque payload of one pending effect for the worker. */
|
|
38
|
+
export function sheetsPayloadValidationError(effect) {
|
|
39
|
+
try {
|
|
40
|
+
toProviderEffect(effect);
|
|
41
|
+
return absentValue();
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return presentValue(safeProviderErrorMessage(error));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Classifies one pending effect as an append-only fast candidate.
|
|
49
|
+
*
|
|
50
|
+
* A fast append requires an empty visible baseline (`createIfMissing` with
|
|
51
|
+
* revision 0 and an empty visible hash) plus a new System_State entity row or
|
|
52
|
+
* a new Sync_Conflicts resolution row.
|
|
53
|
+
*/
|
|
54
|
+
export function isSheetsFastAppendCandidate(effect) {
|
|
55
|
+
try {
|
|
56
|
+
return isFastAppendEffect(toProviderEffect(effect));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Classifies one converted provider effect as an append-only fast candidate. */
|
|
63
|
+
export function isFastAppendEffect(effect) {
|
|
64
|
+
const emptyVisibleBaseline = effect.payload.createIfMissing &&
|
|
65
|
+
effect.expectedVisibleRevision === NON_NEGATIVE_SAFE_INTEGER_MINIMUM &&
|
|
66
|
+
effect.expectedVisibleHash === "";
|
|
67
|
+
if (!emptyVisibleBaseline)
|
|
68
|
+
return false;
|
|
69
|
+
return (effect.effectKind === EFFECT_KINDS.SYSTEM_PROJECTION &&
|
|
70
|
+
effect.projection === SYNC_PROJECTIONS.SYSTEM_STATE &&
|
|
71
|
+
effect.targetKind === EFFECT_TARGET_KINDS.ENTITY) || (effect.effectKind === EFFECT_KINDS.RESOLUTION_PROJECTION &&
|
|
72
|
+
effect.projection === SYNC_PROJECTIONS.SYNC_CONFLICTS &&
|
|
73
|
+
effect.targetKind === EFFECT_TARGET_KINDS.CONFLICT);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Dispatcher over a `SyncEffectWorkerProvider` and the host storage adapter.
|
|
77
|
+
*
|
|
78
|
+
* The storage adapter backs the candidate gate and the dispatch authority
|
|
79
|
+
* claim; both are optional host extensions the worker may skip.
|
|
80
|
+
*/
|
|
81
|
+
export class SheetsEffectDispatcher {
|
|
82
|
+
provider;
|
|
83
|
+
storage;
|
|
84
|
+
constructor(options) {
|
|
85
|
+
this.provider = options.provider;
|
|
86
|
+
this.storage = options.storage;
|
|
87
|
+
}
|
|
88
|
+
routeKeyFor(effect) {
|
|
89
|
+
return sheetsRouteKeyFor(effect);
|
|
90
|
+
}
|
|
91
|
+
isFastAppendCandidate(effect) {
|
|
92
|
+
return isSheetsFastAppendCandidate(effect);
|
|
93
|
+
}
|
|
94
|
+
payloadValidationError(effect) {
|
|
95
|
+
return sheetsPayloadValidationError(effect);
|
|
96
|
+
}
|
|
97
|
+
/** Dispatches append-only rows through the idempotent provider batch operation. */
|
|
98
|
+
async fastAppend(request) {
|
|
99
|
+
const providerRequest = buildFastAppendRowsRequest(request.effects);
|
|
100
|
+
let response;
|
|
101
|
+
try {
|
|
102
|
+
response = await this.provider.fastAppendRows(providerRequest);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw toDispatchTransportError(error);
|
|
106
|
+
}
|
|
107
|
+
const byEffect = new Map(request.effects.map((effect) => [effect.effect_id, effect]));
|
|
108
|
+
const results = [];
|
|
109
|
+
for (const result of response.results) {
|
|
110
|
+
const pending = byEffect.get(result.effectId);
|
|
111
|
+
if (pending === undefined)
|
|
112
|
+
continue;
|
|
113
|
+
const effect = toProviderEffect(pending);
|
|
114
|
+
const evidence = receiptEvidence(result);
|
|
115
|
+
if (evidence === undefined) {
|
|
116
|
+
results.push({
|
|
117
|
+
effectId: result.effectId,
|
|
118
|
+
status: "delivery_uncertain",
|
|
119
|
+
reason: presentValue("Fast append did not return receipt-backed visible evidence."),
|
|
120
|
+
});
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (evidence.visibleHash !== effect.payload.targetVisibleHash) {
|
|
124
|
+
// The append receipt did not match the effect target: the remote row
|
|
125
|
+
// does not carry the acknowledged state, so the worker closes the
|
|
126
|
+
// effect as failed rather than trusting the receipt.
|
|
127
|
+
results.push({ effectId: result.effectId, status: "applied_target_mismatch" });
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
results.push({
|
|
131
|
+
effectId: result.effectId,
|
|
132
|
+
status: "applied",
|
|
133
|
+
visibleRevision: evidence.visibleRevision,
|
|
134
|
+
visibleHash: evidence.visibleHash,
|
|
135
|
+
fieldHashes: fieldHashesFor(effect),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return { results, hasMore: response.hasMore, ...(response.timing === undefined ? {} : { timing: response.timing }) };
|
|
139
|
+
}
|
|
140
|
+
/** Dispatches one regular effect batch through the provider. */
|
|
141
|
+
async apply(request) {
|
|
142
|
+
const effects = request.effects.map((pending) => toProviderEffect(pending));
|
|
143
|
+
const providerRequest = {
|
|
144
|
+
physicalSheetId: effects[0].physicalSheetId,
|
|
145
|
+
sheetName: effects[0].payload.sheetName,
|
|
146
|
+
registeredRange: effects[0].payload.registeredRange,
|
|
147
|
+
projection: effects[0].projection,
|
|
148
|
+
schemaVersion: effects[0].payload.schemaVersion,
|
|
149
|
+
postconditionMode: SYNC_POSTCONDITION_MODES.DEFERRED,
|
|
150
|
+
effects,
|
|
151
|
+
};
|
|
152
|
+
let response;
|
|
153
|
+
try {
|
|
154
|
+
response = await this.provider.applyEffects(providerRequest);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
throw toDispatchTransportError(error);
|
|
158
|
+
}
|
|
159
|
+
const byEffect = new Map(effects.map((effect) => [effect.effectId, effect]));
|
|
160
|
+
const results = [];
|
|
161
|
+
for (const result of response.results) {
|
|
162
|
+
results.push(translateApplyResult(result, byEffect.get(result.effectId)));
|
|
163
|
+
}
|
|
164
|
+
return { results, hasMore: response.hasMore, ...(response.timing === undefined ? {} : { timing: response.timing }) };
|
|
165
|
+
}
|
|
166
|
+
/** Reads back response-loss effects so the worker can settle them safely. */
|
|
167
|
+
async readPostconditions(request) {
|
|
168
|
+
const effects = request.effects.map((pending) => toProviderEffect(pending));
|
|
169
|
+
const providerRequest = {
|
|
170
|
+
physicalSheetId: effects[0].physicalSheetId,
|
|
171
|
+
sheetName: effects[0].payload.sheetName,
|
|
172
|
+
registeredRange: effects[0].payload.registeredRange,
|
|
173
|
+
projection: effects[0].projection,
|
|
174
|
+
schemaVersion: effects[0].payload.schemaVersion,
|
|
175
|
+
effects,
|
|
176
|
+
};
|
|
177
|
+
let response;
|
|
178
|
+
try {
|
|
179
|
+
response = await this.provider.readEffectPostconditions(providerRequest);
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
throw toDispatchTransportError(error);
|
|
183
|
+
}
|
|
184
|
+
const byEffect = new Map(effects.map((effect) => [effect.effectId, effect]));
|
|
185
|
+
const results = response.map((result) => ({
|
|
186
|
+
effectId: result.effectId,
|
|
187
|
+
payloadHash: result.payloadHash,
|
|
188
|
+
postcondition: translatePostcondition(result.postcondition, byEffect.get(result.effectId)),
|
|
189
|
+
}));
|
|
190
|
+
return { results };
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Preserves active User_Input candidates before remote dispatch: reconcile
|
|
194
|
+
* or delete effects that would overwrite a candidate-owned field are
|
|
195
|
+
* blocked locally before any remote compare-and-set runs.
|
|
196
|
+
*/
|
|
197
|
+
async gate(items) {
|
|
198
|
+
const allowed = [];
|
|
199
|
+
const blocked = [];
|
|
200
|
+
for (const item of items) {
|
|
201
|
+
let effect;
|
|
202
|
+
try {
|
|
203
|
+
effect = toProviderEffect(item.pending);
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
allowed.push(item.pending.effect_id);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (!isCandidateProtectingUserInputEffect(effect)) {
|
|
210
|
+
allowed.push(item.pending.effect_id);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
const rowBinding = effect.rowBindingId;
|
|
214
|
+
if (rowBinding.kind !== "present") {
|
|
215
|
+
allowed.push(item.pending.effect_id);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const fieldNames = Object.keys(effect.payload.fields);
|
|
219
|
+
const blockedByCandidate = await this.storage.read(({ sql }) => hasActiveUserInputCandidateWithSql(sql, {
|
|
220
|
+
physicalSheetId: effect.physicalSheetId,
|
|
221
|
+
projection: SYNC_PROJECTIONS.USER_INPUT,
|
|
222
|
+
rowBindingId: rowBinding.value,
|
|
223
|
+
fieldNames,
|
|
224
|
+
openConflictStatus: CONFLICT_STATUSES.OPEN,
|
|
225
|
+
rebasedConflictStatus: CONFLICT_STATUSES.NEEDS_REBASE,
|
|
226
|
+
}));
|
|
227
|
+
if (blockedByCandidate)
|
|
228
|
+
blocked.push(item.pending.effect_id);
|
|
229
|
+
else
|
|
230
|
+
allowed.push(item.pending.effect_id);
|
|
231
|
+
}
|
|
232
|
+
return { allowed, blocked };
|
|
233
|
+
}
|
|
234
|
+
/** Refreshes the dispatch authority claim for one physical sheet. */
|
|
235
|
+
async ensureAuthority(fence, physicalSheetId, ownerId) {
|
|
236
|
+
const result = await ensureSpreadsheetAuthorityWithAdapter(this.storage, {
|
|
237
|
+
...fence,
|
|
238
|
+
physicalSheetId,
|
|
239
|
+
ownerId,
|
|
240
|
+
});
|
|
241
|
+
return result.kind === "claimed";
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function buildFastAppendRowsRequest(effects) {
|
|
245
|
+
const converted = effects.map((pending) => toProviderEffect(pending));
|
|
246
|
+
const first = converted[0];
|
|
247
|
+
return {
|
|
248
|
+
physicalSheetId: first.physicalSheetId,
|
|
249
|
+
sheetName: first.payload.sheetName,
|
|
250
|
+
registeredRange: first.payload.registeredRange,
|
|
251
|
+
projection: first.projection,
|
|
252
|
+
schemaVersion: first.payload.schemaVersion,
|
|
253
|
+
rows: converted.map((effect) => ({
|
|
254
|
+
effectId: effect.effectId,
|
|
255
|
+
payloadHash: effect.payloadHash,
|
|
256
|
+
anchor: effect.payload.targetAnchor,
|
|
257
|
+
fields: effect.payload.fields,
|
|
258
|
+
})),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function translateApplyResult(result, effect) {
|
|
262
|
+
const base = {
|
|
263
|
+
effectId: result.effectId,
|
|
264
|
+
payloadHash: result.payloadHash,
|
|
265
|
+
};
|
|
266
|
+
if ((result.status === SYNC_EFFECT_RESULT_STATUSES.APPLIED ||
|
|
267
|
+
result.status === SYNC_EFFECT_RESULT_STATUSES.ALREADY_APPLIED)) {
|
|
268
|
+
const evidence = verifiedSuccessEvidence(result, effect);
|
|
269
|
+
if (evidence !== undefined) {
|
|
270
|
+
return {
|
|
271
|
+
...base,
|
|
272
|
+
status: result.status,
|
|
273
|
+
visibleRevision: evidence.visibleRevision,
|
|
274
|
+
visibleHash: evidence.visibleHash,
|
|
275
|
+
fieldHashes: evidence.fieldHashes,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
// A success label without the acknowledged target state is not enough to
|
|
279
|
+
// close a durable effect; the worker treats it like a lost response and
|
|
280
|
+
// reads back first.
|
|
281
|
+
return {
|
|
282
|
+
...base,
|
|
283
|
+
status: "delivery_uncertain",
|
|
284
|
+
reason: presentValue("Provider applied result did not include receipt-backed visible evidence."),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
if (result.status === SYNC_EFFECT_RESULT_STATUSES.SUPERSEDED) {
|
|
288
|
+
return { ...base, status: "superseded", reason: result.reason };
|
|
289
|
+
}
|
|
290
|
+
if (result.status === SYNC_EFFECT_RESULT_STATUSES.GUARD_MISMATCH) {
|
|
291
|
+
return { ...base, status: "guard_mismatch", reason: result.reason };
|
|
292
|
+
}
|
|
293
|
+
if (result.status === SYNC_EFFECT_RESULT_STATUSES.REPAIR_REOBSERVE) {
|
|
294
|
+
return { ...base, status: "repair_reobserve", reason: result.reason };
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
...base,
|
|
298
|
+
status: result.status === SYNC_EFFECT_RESULT_STATUSES.SCHEMA_ERROR
|
|
299
|
+
? "schema_error"
|
|
300
|
+
: "retryable_error",
|
|
301
|
+
reason: result.reason,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function translatePostcondition(postcondition, effect) {
|
|
305
|
+
if (postcondition.disposition === SYNC_POSTCONDITION_DISPOSITIONS.APPLIED &&
|
|
306
|
+
postcondition.visibleRevision.kind === "present" &&
|
|
307
|
+
postcondition.visibleHash.kind === "present" &&
|
|
308
|
+
effect !== undefined &&
|
|
309
|
+
postcondition.visibleHash.value === effect.payload.targetVisibleHash) {
|
|
310
|
+
return {
|
|
311
|
+
disposition: "applied",
|
|
312
|
+
visibleRevision: postcondition.visibleRevision.value,
|
|
313
|
+
visibleHash: postcondition.visibleHash.value,
|
|
314
|
+
fieldHashes: fieldHashesFor(effect),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
if (postcondition.disposition === SYNC_POSTCONDITION_DISPOSITIONS.APPLIED) {
|
|
318
|
+
if (effect !== undefined &&
|
|
319
|
+
postcondition.visibleRevision.kind === "present" &&
|
|
320
|
+
postcondition.visibleHash.kind === "present") {
|
|
321
|
+
return { disposition: "applied_target_mismatch" };
|
|
322
|
+
}
|
|
323
|
+
return { disposition: "applied_without_visible_state" };
|
|
324
|
+
}
|
|
325
|
+
if (postcondition.disposition === SYNC_POSTCONDITION_DISPOSITIONS.CHANGED) {
|
|
326
|
+
return {
|
|
327
|
+
disposition: "changed",
|
|
328
|
+
...(postcondition.reason === undefined ? {} : { reason: postcondition.reason }),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (postcondition.disposition === SYNC_POSTCONDITION_DISPOSITIONS.UNAPPLIED) {
|
|
332
|
+
return {
|
|
333
|
+
disposition: "unapplied",
|
|
334
|
+
...(postcondition.reason === undefined ? {} : { reason: postcondition.reason }),
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
disposition: "unavailable",
|
|
339
|
+
...(postcondition.reason === undefined ? {} : { reason: postcondition.reason }),
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Returns verified success evidence only when the provider's success label
|
|
344
|
+
* carries the acknowledged target state: a verified/acknowledged postcondition
|
|
345
|
+
* plus a visible revision and hash that match the effect target.
|
|
346
|
+
*/
|
|
347
|
+
function verifiedSuccessEvidence(result, effect) {
|
|
348
|
+
if (result.postcondition !== SYNC_POSTCONDITION_STATUSES.VERIFIED &&
|
|
349
|
+
result.postcondition !== SYNC_POSTCONDITION_STATUSES.ACKNOWLEDGED) {
|
|
350
|
+
return undefined;
|
|
351
|
+
}
|
|
352
|
+
if (result.visibleRevision.kind !== "present" ||
|
|
353
|
+
result.visibleHash.kind !== "present") {
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
if (effect === undefined ||
|
|
357
|
+
result.visibleHash.value !== effect.payload.targetVisibleHash) {
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
visibleRevision: result.visibleRevision.value,
|
|
362
|
+
visibleHash: result.visibleHash.value,
|
|
363
|
+
fieldHashes: fieldHashesFor(effect),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function receiptEvidence(result) {
|
|
367
|
+
if (result.status !== SYNC_FAST_APPEND_STATUSES.APPLIED ||
|
|
368
|
+
typeof result.visibleRevision !== "number" ||
|
|
369
|
+
!Number.isSafeInteger(result.visibleRevision) ||
|
|
370
|
+
result.visibleRevision < 1 ||
|
|
371
|
+
typeof result.visibleHash !== "string" ||
|
|
372
|
+
result.visibleHash.length === 0) {
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
return { visibleRevision: result.visibleRevision, visibleHash: result.visibleHash };
|
|
376
|
+
}
|
|
377
|
+
function fieldHashesFor(effect) {
|
|
378
|
+
return Object.fromEntries(Object.entries(effect.payload.fields)
|
|
379
|
+
.map(([fieldName, value]) => [fieldName, stableHash(value)]));
|
|
380
|
+
}
|
|
381
|
+
function isCandidateProtectingUserInputEffect(effect) {
|
|
382
|
+
return effect.effectKind === EFFECT_KINDS.CANDIDATE_RECONCILE ||
|
|
383
|
+
effect.effectKind === EFFECT_KINDS.USER_INPUT_DELETE;
|
|
384
|
+
}
|
|
385
|
+
/** Validates and converts one durable outbox row into a provider effect. */
|
|
386
|
+
function toProviderEffect(effect) {
|
|
387
|
+
if (!isSyncEffectKind(effect.effect_kind)) {
|
|
388
|
+
throw new Error("unsupported sync effect kind: " + effect.effect_kind);
|
|
389
|
+
}
|
|
390
|
+
if (!isSyncProjection(effect.projection)) {
|
|
391
|
+
throw new Error("unsupported sync projection: " + effect.projection);
|
|
392
|
+
}
|
|
393
|
+
if (!isEffectTargetKind(effect.target_kind)) {
|
|
394
|
+
throw new Error("unsupported sync effect target kind: " + effect.target_kind);
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
effectId: effect.effect_id,
|
|
398
|
+
payloadHash: effect.payload_hash,
|
|
399
|
+
effectKind: effect.effect_kind,
|
|
400
|
+
physicalSheetId: effect.physical_sheet_id,
|
|
401
|
+
projection: effect.projection,
|
|
402
|
+
targetKind: effect.target_kind,
|
|
403
|
+
targetId: effect.target_id,
|
|
404
|
+
rowBindingId: fromSqlNullable(effect.row_binding_id),
|
|
405
|
+
conflictId: fromSqlNullable(effect.conflict_id),
|
|
406
|
+
expectedVisibleRevision: effect.expected_visible_revision,
|
|
407
|
+
expectedVisibleHash: effect.expected_visible_hash,
|
|
408
|
+
repairGuardHash: fromSqlNullable(effect.repair_guard_hash),
|
|
409
|
+
payload: parseSyncProjectionEffectPayload(effect.payload_json),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
function isSyncEffectKind(value) {
|
|
413
|
+
return value === EFFECT_KINDS.SYSTEM_PROJECTION ||
|
|
414
|
+
value === EFFECT_KINDS.CANDIDATE_RECONCILE ||
|
|
415
|
+
value === EFFECT_KINDS.SYSTEM_REPAIR ||
|
|
416
|
+
value === EFFECT_KINDS.RESOLUTION_PROJECTION ||
|
|
417
|
+
value === EFFECT_KINDS.RESOLUTION_DELETE ||
|
|
418
|
+
value === EFFECT_KINDS.USER_INPUT_DELETE;
|
|
419
|
+
}
|
|
420
|
+
function isSyncProjection(value) {
|
|
421
|
+
return value === SYNC_PROJECTIONS.USER_INPUT ||
|
|
422
|
+
value === SYNC_PROJECTIONS.SYSTEM_STATE ||
|
|
423
|
+
value === SYNC_PROJECTIONS.SYNC_CONFLICTS;
|
|
424
|
+
}
|
|
425
|
+
function isEffectTargetKind(value) {
|
|
426
|
+
return value === EFFECT_TARGET_KINDS.ENTITY ||
|
|
427
|
+
value === EFFECT_TARGET_KINDS.ROW_BINDING ||
|
|
428
|
+
value === EFFECT_TARGET_KINDS.PROJECTION_ROW ||
|
|
429
|
+
value === EFFECT_TARGET_KINDS.CONFLICT;
|
|
430
|
+
}
|
|
431
|
+
function toDispatchTransportError(error) {
|
|
432
|
+
const outcome = classifyTransportOutcome(error);
|
|
433
|
+
return new DispatchTransportError(outcome.kind === TRANSPORT_OUTCOME_KINDS.EXPLICIT_REMOTE_FAILURE
|
|
434
|
+
? "explicit_remote_failure"
|
|
435
|
+
: "delivery_uncertain", outcome.message);
|
|
436
|
+
}
|
|
437
|
+
function safeProviderErrorMessage(error) {
|
|
438
|
+
return error instanceof Error ? error.message.slice(0, 500) : "unknown sync provider failure";
|
|
439
|
+
}
|
|
440
|
+
//# sourceMappingURL=SheetsEffectDispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SheetsEffectDispatcher.js","sourceRoot":"","sources":["../../../../src/application/sync/outbound/SheetsEffectDispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,8 +18,7 @@ import type { SyncSheetsProvisioner, RegisteredSyncProjectionDefinition } from "
|
|
|
18
18
|
import type { SyncSheetsProvider, SyncSheetsTableReader } from "../sheets/syncSheets.js";
|
|
19
19
|
import type { CoordinatorLaneEvent } from "../sheets/mutationCoordinator/laneTelemetry.js";
|
|
20
20
|
import { type GoogleSheetsApiProviderOptions } from "../../../adapter/sheets/providers/google-sheets-api/index.js";
|
|
21
|
-
import { type
|
|
22
|
-
import type { SyncEffectWorkerReport } from "../outbound/effects/SyncEffectWorker.js";
|
|
21
|
+
import { type EffectWorkerSupervisor, type WorkerReport } from "@hikoutei/outbox";
|
|
23
22
|
import { type MappedUserInputPollingReport } from "../../../adapter/persistence/providers/mikro-orm/observation/MikroOrmUserInputPolling.js";
|
|
24
23
|
import { SyncPollingSupervisor } from "./SyncPollingSupervisor.js";
|
|
25
24
|
import type { SyncTimingSink } from "../telemetry/syncTiming.js";
|
|
@@ -53,7 +52,7 @@ export interface InternalSyncServiceOptions {
|
|
|
53
52
|
readonly effectLeaseDurationMs?: number;
|
|
54
53
|
readonly effectIdleIntervalMs?: number;
|
|
55
54
|
readonly onTiming?: SyncTimingSink;
|
|
56
|
-
readonly onEffectReport?: (report:
|
|
55
|
+
readonly onEffectReport?: (report: WorkerReport) => void;
|
|
57
56
|
readonly onEffectError?: (error: unknown) => void;
|
|
58
57
|
readonly pollingIntervalMs?: number;
|
|
59
58
|
/** Maximum interval between metadata-preserving safety scans. */
|
|
@@ -75,7 +74,7 @@ export interface InternalSyncService {
|
|
|
75
74
|
readonly projectionDefinitions: readonly RegisteredSyncProjectionDefinition[];
|
|
76
75
|
/** Retries durable OPEN system-wins commands after predecessors settle. */
|
|
77
76
|
readonly retryDeferredConflicts: () => Promise<number>;
|
|
78
|
-
readonly effectSupervisor:
|
|
77
|
+
readonly effectSupervisor: EffectWorkerSupervisor;
|
|
79
78
|
readonly pollingSupervisor: SyncPollingSupervisor<MappedUserInputPollingReport>;
|
|
80
79
|
stop(): Promise<void>;
|
|
81
80
|
close(): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SyncServiceBootstrap.d.ts","sourceRoot":"","sources":["../../../../src/application/sync/service/SyncServiceBootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAoC,MAAM,wBAAwB,CAAC;AAC/F,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"SyncServiceBootstrap.d.ts","sourceRoot":"","sources":["../../../../src/application/sync/service/SyncServiceBootstrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
import { randomUUID } from "node:crypto";
|
|
15
15
|
import { createInternalHikoutei, } from "../../../api/Hikoutei.js";
|
|
16
16
|
import { getEntityDescriptor, HikouteiEntity as HikouteiEntityToken } from "../../../api/entity.js";
|
|
17
|
+
import { readWriterLeaseWithAdapter, releaseWriterLeaseWithAdapter, } from "../../../infrastructure/storage/index.js";
|
|
18
|
+
import { DEFAULT_MAPPED_WRITER_ROLE, } from "../../orm/persistence/support/contracts.js";
|
|
17
19
|
import { initializeMappedTypedSheetsRuntime, } from "../../../adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.js";
|
|
18
20
|
import { createMikroOrmScalarRuntime, } from "../../../adapter/persistence/providers/mikro-orm/engine/MikroOrmScalarRuntime.js";
|
|
19
21
|
import { MikroOrmScalarPersistenceProvider, } from "../../../adapter/persistence/providers/mikro-orm/api/MikroOrmScalarPersistenceProvider.js";
|
|
@@ -24,11 +26,11 @@ import { autoResolveExistingMappedConflictsWithAdapter, retryOpenMappedConflicts
|
|
|
24
26
|
import { CoordinatedSheetsProvider, } from "../sheets/mutationCoordinator/CoordinatedSheetsProvider.js";
|
|
25
27
|
import { GoogleSheetsApiSyncProvider, } from "../../../adapter/sheets/providers/google-sheets-api/index.js";
|
|
26
28
|
import { GOOGLE_SHEETS_API_DEFAULTS } from "../../../adapter/sheets/providers/google-sheets-api/constants.js";
|
|
27
|
-
import {
|
|
29
|
+
import { createEffectWorkerSupervisor, APPEND_DISPATCH_THROTTLE_INTERVAL_MS, DEFAULT_EFFECT_LEASE_DURATION_MS, DEFAULT_WORKER_ROLE, DEFAULT_WRITER_LEASE_DURATION_MS, EFFECT_LEASE_PROVIDER_HEADROOM_MS, FAST_APPEND_BATCH_CANDIDATE_LIMIT, LOOKUP_RESULT_KINDS, safeErrorMessage, } from "@hikoutei/outbox";
|
|
30
|
+
import { SheetsEffectDispatcher } from "../outbound/SheetsEffectDispatcher.js";
|
|
28
31
|
import { MAPPED_USER_INPUT_POLL_MODES, pollMappedUserInputWithMikroOrm, } from "../../../adapter/persistence/providers/mikro-orm/observation/MikroOrmUserInputPolling.js";
|
|
29
32
|
import { SyncPollingSupervisor } from "./SyncPollingSupervisor.js";
|
|
30
33
|
import { SYNC_SERVICE_ERROR_CODES, SyncServiceError, } from "./errors.js";
|
|
31
|
-
import { APPEND_DISPATCH_THROTTLE_INTERVAL_MS, DEFAULT_EFFECT_LEASE_DURATION_MS, DEFAULT_WRITER_LEASE_DURATION_MS, EFFECT_LEASE_PROVIDER_HEADROOM_MS, FAST_APPEND_BATCH_CANDIDATE_LIMIT, } from "../outbound/effects/SyncEffectWorkerConstants.js";
|
|
32
34
|
const DEFAULT_POLLING_FULL_SCAN_INTERVAL_MS = 60_000;
|
|
33
35
|
/**
|
|
34
36
|
* Opens, provisions, and starts one internal sync service.
|
|
@@ -41,6 +43,11 @@ export async function createInternalSyncService(options) {
|
|
|
41
43
|
validateServiceOptions(options, descriptors);
|
|
42
44
|
const generated = createMikroOrmScalarRuntime(options.entities, options.projections);
|
|
43
45
|
const writer = createWriterOptions(options);
|
|
46
|
+
// The effect worker claims its own lease role with the same runtime identity
|
|
47
|
+
// as the mapped writer unless the caller pinned an explicit worker id; one
|
|
48
|
+
// resolved identity is reused by the supervisor and by the graceful-shutdown
|
|
49
|
+
// lease release so both roles expire together on stop().
|
|
50
|
+
const effectWorkerId = options.workerId ?? writer.writerId;
|
|
44
51
|
const runtime = await initializeMappedTypedSheetsRuntime({
|
|
45
52
|
dbName: options.dbName,
|
|
46
53
|
entities: generated.entities,
|
|
@@ -55,10 +62,14 @@ export async function createInternalSyncService(options) {
|
|
|
55
62
|
const remote = createRemoteProvider(options, projectionDefinitions);
|
|
56
63
|
await provisionRegisteredSyncSheets(remote.provisioner, projectionDefinitions);
|
|
57
64
|
await autoResolveExistingMappedConflictsWithAdapter(runtime.storage, runtime.mappings.mappings, resolveTypedSheetsEntityWriterOptions(writer));
|
|
58
|
-
const effectSupervisor =
|
|
65
|
+
const effectSupervisor = createEffectWorkerSupervisor({
|
|
59
66
|
storage: runtime.storage,
|
|
60
|
-
|
|
67
|
+
dispatcher: new SheetsEffectDispatcher({
|
|
68
|
+
provider: remote.provider,
|
|
69
|
+
storage: runtime.storage,
|
|
70
|
+
}),
|
|
61
71
|
...optionalWorkerOptions(options),
|
|
72
|
+
workerId: effectWorkerId,
|
|
62
73
|
});
|
|
63
74
|
const pollingFullScanIntervalMs = options.pollingFullScanIntervalMs
|
|
64
75
|
?? DEFAULT_POLLING_FULL_SCAN_INTERVAL_MS;
|
|
@@ -107,8 +118,24 @@ export async function createInternalSyncService(options) {
|
|
|
107
118
|
// Stop inbound reads first, then the outbound worker. Both supervisors
|
|
108
119
|
// drain manual and background passes before SQLite is closed.
|
|
109
120
|
stopPromise = (async () => {
|
|
110
|
-
|
|
111
|
-
|
|
121
|
+
try {
|
|
122
|
+
await pollingSupervisor.stop();
|
|
123
|
+
await effectSupervisor.stop();
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
// Graceful-shutdown handoff: expire this runtime's own leases so the
|
|
127
|
+
// next runtime on the same SQLite file claims immediately through the
|
|
128
|
+
// TAKEOVER path. Abnormal exits keep the full lease window, and the
|
|
129
|
+
// row is never deleted so authority epoch ordering stays monotonic.
|
|
130
|
+
// The release ALWAYS runs, even when a supervisor stop fails, so a
|
|
131
|
+
// restart inside the lease window never fails with
|
|
132
|
+
// WRITER_LEASE_UNAVAILABLE; a failing release only logs and never
|
|
133
|
+
// masks the supervisor error. `stopped` still flips only when both
|
|
134
|
+
// supervisor stops complete, so a rejected stop leaves a retryable
|
|
135
|
+
// close() that re-runs the stops and the release.
|
|
136
|
+
await expireRuntimeWriterLeases(runtime.storage, writer, effectWorkerId)
|
|
137
|
+
.catch(() => undefined);
|
|
138
|
+
}
|
|
112
139
|
stopped = true;
|
|
113
140
|
stopPromise = undefined;
|
|
114
141
|
})().catch((error) => {
|
|
@@ -133,6 +160,15 @@ export async function createInternalSyncService(options) {
|
|
|
133
160
|
};
|
|
134
161
|
}
|
|
135
162
|
catch (error) {
|
|
163
|
+
// Conflict-route registration claims this runtime's mapped-role writer
|
|
164
|
+
// lease before remote provisioning runs; a provisioning failure would
|
|
165
|
+
// otherwise leave the claim valid for the full lease window and every
|
|
166
|
+
// immediate retry would fail at the same claim with
|
|
167
|
+
// WRITER_LEASE_UNAVAILABLE. Expire the claimed leases (CAS-guarded,
|
|
168
|
+
// warn-and-continue) so the retry takes over at once; the extra guard
|
|
169
|
+
// ensures this can never mask the original startup error.
|
|
170
|
+
await expireRuntimeWriterLeases(runtime.storage, writer, effectWorkerId)
|
|
171
|
+
.catch(() => undefined);
|
|
136
172
|
await runtime.storage.close(true).catch(() => undefined);
|
|
137
173
|
throw error;
|
|
138
174
|
}
|
|
@@ -270,6 +306,55 @@ function createWriterOptions(options) {
|
|
|
270
306
|
...(options.onTiming === undefined ? {} : { onTiming: options.onTiming }),
|
|
271
307
|
};
|
|
272
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Expires this runtime's own writer leases on graceful shutdown.
|
|
311
|
+
*
|
|
312
|
+
* Each release is CAS-guarded on the exact lease row this runtime holds
|
|
313
|
+
* (role, writer id, epoch, fencing token), so a concurrent takeover or a
|
|
314
|
+
* previously crashed runtime's stale row is never expired. The observation
|
|
315
|
+
* poller and the auto conflict resolver claim under the mapped writer role
|
|
316
|
+
* with the same writer id, so the single mapped-role release covers them.
|
|
317
|
+
* A failing release only logs a classified warning and never aborts stop().
|
|
318
|
+
*/
|
|
319
|
+
async function expireRuntimeWriterLeases(storage, writer, effectWorkerId) {
|
|
320
|
+
const now = writer.now?.() ?? Date.now();
|
|
321
|
+
// Every claim site wired into the bootstrap must be mirrored here:
|
|
322
|
+
// conflict route registration (registerSyncConflictProjectionRoutes), auto
|
|
323
|
+
// conflict resolution (autoSystemConflictResolution), mapped flush planning
|
|
324
|
+
// and projection registration (flushCoordinator,
|
|
325
|
+
// mappedPersistenceContext), and observation polling
|
|
326
|
+
// (MikroOrmUserInputPolling) claim under the mapped writer role with
|
|
327
|
+
// writer.writerId; the effect worker supervisor claims under
|
|
328
|
+
// DEFAULT_WORKER_ROLE with effectWorkerId. A claim added elsewhere must be
|
|
329
|
+
// added to this list, or graceful close leaves it leased for the window.
|
|
330
|
+
const ownLeases = [
|
|
331
|
+
{ role: DEFAULT_MAPPED_WRITER_ROLE, writerId: writer.writerId },
|
|
332
|
+
{ role: DEFAULT_WORKER_ROLE, writerId: effectWorkerId },
|
|
333
|
+
];
|
|
334
|
+
for (const { role, writerId } of ownLeases) {
|
|
335
|
+
try {
|
|
336
|
+
const found = await readWriterLeaseWithAdapter(storage, role);
|
|
337
|
+
if (found.kind !== LOOKUP_RESULT_KINDS.FOUND || found.value.writerId !== writerId) {
|
|
338
|
+
// No row, or the lease was already taken over by a newer writer:
|
|
339
|
+
// nothing this runtime owns to expire.
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
const released = await releaseWriterLeaseWithAdapter(storage, {
|
|
343
|
+
role,
|
|
344
|
+
writerId,
|
|
345
|
+
writerEpoch: found.value.writerEpoch,
|
|
346
|
+
fencingToken: found.value.fencingToken,
|
|
347
|
+
now,
|
|
348
|
+
});
|
|
349
|
+
if (!released) {
|
|
350
|
+
console.warn(`[sync-service] writer lease for role "${role}" changed ownership during shutdown; skipping release.`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
console.warn(`[sync-service] writer lease release failed for role "${role}": ${safeErrorMessage(error)}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
273
358
|
function optionalWorkerOptions(options) {
|
|
274
359
|
// The knobs are tied to the ACTIVE provider: the full direct Google
|
|
275
360
|
// provider's timeouts (its defaults when omitted). The worker provider
|