hikoutei 0.2.0
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/LICENSE +21 -0
- package/README.ja.md +135 -0
- package/README.ko.md +138 -0
- package/README.md +143 -0
- package/apps-script/gateway/Code.gs +412 -0
- package/dist/adapter/persistence/contracts/index.d.ts +3 -0
- package/dist/adapter/persistence/contracts/index.d.ts.map +1 -0
- package/dist/adapter/persistence/contracts/index.js +3 -0
- package/dist/adapter/persistence/contracts/index.js.map +1 -0
- package/dist/adapter/persistence/contracts/sql.d.ts +50 -0
- package/dist/adapter/persistence/contracts/sql.d.ts.map +1 -0
- package/dist/adapter/persistence/contracts/sql.js +9 -0
- package/dist/adapter/persistence/contracts/sql.js.map +1 -0
- package/dist/adapter/persistence/index.d.ts +2 -0
- package/dist/adapter/persistence/index.d.ts.map +1 -0
- package/dist/adapter/persistence/index.js +2 -0
- package/dist/adapter/persistence/index.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.d.ts +53 -0
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.js +105 -0
- package/dist/adapter/persistence/providers/mikro-orm/api/MikroOrmTypedSheetsEntityManager.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.d.ts +39 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.js +54 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmMappedTypedSheets.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.d.ts +38 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.js +55 -0
- package/dist/adapter/persistence/providers/mikro-orm/engine/MikroOrmTypedSheetsEngine.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/index.d.ts +16 -0
- package/dist/adapter/persistence/providers/mikro-orm/index.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/index.js +9 -0
- package/dist/adapter/persistence/providers/mikro-orm/index.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/observation/MikroOrmMappedObservation.d.ts +27 -0
- package/dist/adapter/persistence/providers/mikro-orm/observation/MikroOrmMappedObservation.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/observation/MikroOrmMappedObservation.js +67 -0
- package/dist/adapter/persistence/providers/mikro-orm/observation/MikroOrmMappedObservation.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.d.ts +19 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.js +67 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmFlushChanges.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.d.ts +78 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.js +121 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteAdapter.js.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteSchema.d.ts +20 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteSchema.d.ts.map +1 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteSchema.js +125 -0
- package/dist/adapter/persistence/providers/mikro-orm/storage/MikroOrmSqliteSchema.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/errors.d.ts +42 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/errors.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/errors.js +51 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/errors.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/index.d.ts +22 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/index.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/index.js +12 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/index.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/effect/effectOperation.d.ts +42 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/effect/effectOperation.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/effect/effectOperation.js +161 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/effect/effectOperation.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/effect/effectOperationScript.d.ts +3 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/effect/effectOperationScript.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/effect/effectOperationScript.js +692 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/effect/effectOperationScript.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/observation/observationOperation.d.ts +29 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/observation/observationOperation.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/observation/observationOperation.js +538 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/observation/observationOperation.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/read/tableReadOperation.d.ts +12 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/read/tableReadOperation.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/read/tableReadOperation.js +190 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/read/tableReadOperation.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/write/fastAppendOperation.d.ts +23 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/write/fastAppendOperation.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/write/fastAppendOperation.js +166 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/operations/write/fastAppendOperation.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/codeGsProtocol.d.ts +50 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/codeGsProtocol.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/codeGsProtocol.js +60 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/codeGsProtocol.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/constants.d.ts +44 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/constants.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/constants.js +40 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/constants.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/syncProtocol.d.ts +7 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/syncProtocol.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/syncProtocol.js +47 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/syncProtocol.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/timing.d.ts +5 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/timing.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/timing.js +54 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/timing.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/types.d.ts +5 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/types.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/types.js +2 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/types.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/validation.d.ts +10 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/validation.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/validation.js +35 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/protocol/validation.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/transport/operationClient.d.ts +67 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/transport/operationClient.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/transport/operationClient.js +204 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/transport/operationClient.js.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/transport/operationSyncGateway.d.ts +78 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/transport/operationSyncGateway.d.ts.map +1 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/transport/operationSyncGateway.js +343 -0
- package/dist/adapter/sheets/providers/apps-script-gateway/transport/operationSyncGateway.js.map +1 -0
- package/dist/application/index.d.ts +4 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +4 -0
- package/dist/application/index.js.map +1 -0
- package/dist/application/orm/api/TypedSheetsOrm.d.ts +71 -0
- package/dist/application/orm/api/TypedSheetsOrm.d.ts.map +1 -0
- package/dist/application/orm/api/TypedSheetsOrm.js +116 -0
- package/dist/application/orm/api/TypedSheetsOrm.js.map +1 -0
- package/dist/application/orm/api/contracts.d.ts +118 -0
- package/dist/application/orm/api/contracts.d.ts.map +1 -0
- package/dist/application/orm/api/contracts.js +21 -0
- package/dist/application/orm/api/contracts.js.map +1 -0
- package/dist/application/orm/errors.d.ts +35 -0
- package/dist/application/orm/errors.d.ts.map +1 -0
- package/dist/application/orm/errors.js +35 -0
- package/dist/application/orm/errors.js.map +1 -0
- package/dist/application/orm/index.d.ts +13 -0
- package/dist/application/orm/index.d.ts.map +1 -0
- package/dist/application/orm/index.js +7 -0
- package/dist/application/orm/index.js.map +1 -0
- package/dist/application/orm/mapping/contracts.d.ts +113 -0
- package/dist/application/orm/mapping/contracts.d.ts.map +1 -0
- package/dist/application/orm/mapping/contracts.js +8 -0
- package/dist/application/orm/mapping/contracts.js.map +1 -0
- package/dist/application/orm/mapping/definition.d.ts +16 -0
- package/dist/application/orm/mapping/definition.d.ts.map +1 -0
- package/dist/application/orm/mapping/definition.js +145 -0
- package/dist/application/orm/mapping/definition.js.map +1 -0
- package/dist/application/orm/mapping/entityMapping.d.ts +13 -0
- package/dist/application/orm/mapping/entityMapping.d.ts.map +1 -0
- package/dist/application/orm/mapping/entityMapping.js +13 -0
- package/dist/application/orm/mapping/entityMapping.js.map +1 -0
- package/dist/application/orm/mapping/identity.d.ts +14 -0
- package/dist/application/orm/mapping/identity.d.ts.map +1 -0
- package/dist/application/orm/mapping/identity.js +37 -0
- package/dist/application/orm/mapping/identity.js.map +1 -0
- package/dist/application/orm/mapping/observationMapping.d.ts +42 -0
- package/dist/application/orm/mapping/observationMapping.d.ts.map +1 -0
- package/dist/application/orm/mapping/observationMapping.js +65 -0
- package/dist/application/orm/mapping/observationMapping.js.map +1 -0
- package/dist/application/orm/mapping/projection.d.ts +22 -0
- package/dist/application/orm/mapping/projection.d.ts.map +1 -0
- package/dist/application/orm/mapping/projection.js +74 -0
- package/dist/application/orm/mapping/projection.js.map +1 -0
- package/dist/application/orm/mapping/registry.d.ts +15 -0
- package/dist/application/orm/mapping/registry.d.ts.map +1 -0
- package/dist/application/orm/mapping/registry.js +38 -0
- package/dist/application/orm/mapping/registry.js.map +1 -0
- package/dist/application/orm/mapping/values.d.ts +17 -0
- package/dist/application/orm/mapping/values.d.ts.map +1 -0
- package/dist/application/orm/mapping/values.js +141 -0
- package/dist/application/orm/mapping/values.js.map +1 -0
- package/dist/application/orm/persistence/flush/flushCoordinator.d.ts +28 -0
- package/dist/application/orm/persistence/flush/flushCoordinator.d.ts.map +1 -0
- package/dist/application/orm/persistence/flush/flushCoordinator.js +170 -0
- package/dist/application/orm/persistence/flush/flushCoordinator.js.map +1 -0
- package/dist/application/orm/persistence/flush/mappedFlushCoordinator.d.ts +9 -0
- package/dist/application/orm/persistence/flush/mappedFlushCoordinator.d.ts.map +1 -0
- package/dist/application/orm/persistence/flush/mappedFlushCoordinator.js +8 -0
- package/dist/application/orm/persistence/flush/mappedFlushCoordinator.js.map +1 -0
- package/dist/application/orm/persistence/lifecycle/entityLifecycle.d.ts +12 -0
- package/dist/application/orm/persistence/lifecycle/entityLifecycle.d.ts.map +1 -0
- package/dist/application/orm/persistence/lifecycle/entityLifecycle.js +134 -0
- package/dist/application/orm/persistence/lifecycle/entityLifecycle.js.map +1 -0
- package/dist/application/orm/persistence/projection/projectionEffects.d.ts +21 -0
- package/dist/application/orm/persistence/projection/projectionEffects.d.ts.map +1 -0
- package/dist/application/orm/persistence/projection/projectionEffects.js +203 -0
- package/dist/application/orm/persistence/projection/projectionEffects.js.map +1 -0
- package/dist/application/orm/persistence/support/businessKeys.d.ts +17 -0
- package/dist/application/orm/persistence/support/businessKeys.d.ts.map +1 -0
- package/dist/application/orm/persistence/support/businessKeys.js +72 -0
- package/dist/application/orm/persistence/support/businessKeys.js.map +1 -0
- package/dist/application/orm/persistence/support/canonicalState.d.ts +22 -0
- package/dist/application/orm/persistence/support/canonicalState.d.ts.map +1 -0
- package/dist/application/orm/persistence/support/canonicalState.js +88 -0
- package/dist/application/orm/persistence/support/canonicalState.js.map +1 -0
- package/dist/application/orm/persistence/support/contracts.d.ts +139 -0
- package/dist/application/orm/persistence/support/contracts.d.ts.map +1 -0
- package/dist/application/orm/persistence/support/contracts.js +93 -0
- package/dist/application/orm/persistence/support/contracts.js.map +1 -0
- package/dist/application/orm/persistence/support/helpers.d.ts +32 -0
- package/dist/application/orm/persistence/support/helpers.d.ts.map +1 -0
- package/dist/application/orm/persistence/support/helpers.js +66 -0
- package/dist/application/orm/persistence/support/helpers.js.map +1 -0
- package/dist/application/orm/persistence/support/timing.d.ts +20 -0
- package/dist/application/orm/persistence/support/timing.d.ts.map +1 -0
- package/dist/application/orm/persistence/support/timing.js +55 -0
- package/dist/application/orm/persistence/support/timing.js.map +1 -0
- package/dist/application/sync/gateway/SyncGatewayBootstrap.d.ts +46 -0
- package/dist/application/sync/gateway/SyncGatewayBootstrap.d.ts.map +1 -0
- package/dist/application/sync/gateway/SyncGatewayBootstrap.js +93 -0
- package/dist/application/sync/gateway/SyncGatewayBootstrap.js.map +1 -0
- package/dist/application/sync/gateway/constants.d.ts +65 -0
- package/dist/application/sync/gateway/constants.d.ts.map +1 -0
- package/dist/application/sync/gateway/constants.js +57 -0
- package/dist/application/sync/gateway/constants.js.map +1 -0
- package/dist/application/sync/gateway/errors.d.ts +15 -0
- package/dist/application/sync/gateway/errors.d.ts.map +1 -0
- package/dist/application/sync/gateway/errors.js +16 -0
- package/dist/application/sync/gateway/errors.js.map +1 -0
- package/dist/application/sync/gateway/syncGateway.d.ts +313 -0
- package/dist/application/sync/gateway/syncGateway.d.ts.map +1 -0
- package/dist/application/sync/gateway/syncGateway.js +202 -0
- package/dist/application/sync/gateway/syncGateway.js.map +1 -0
- package/dist/application/sync/gateway/validation.d.ts +17 -0
- package/dist/application/sync/gateway/validation.d.ts.map +1 -0
- package/dist/application/sync/gateway/validation.js +60 -0
- package/dist/application/sync/gateway/validation.js.map +1 -0
- package/dist/application/sync/inbound/polling/SimpleSheetPolling.d.ts +72 -0
- package/dist/application/sync/inbound/polling/SimpleSheetPolling.d.ts.map +1 -0
- package/dist/application/sync/inbound/polling/SimpleSheetPolling.js +272 -0
- package/dist/application/sync/inbound/polling/SimpleSheetPolling.js.map +1 -0
- package/dist/application/sync/index.d.ts +16 -0
- package/dist/application/sync/index.d.ts.map +1 -0
- package/dist/application/sync/index.js +9 -0
- package/dist/application/sync/index.js.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts +106 -0
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.d.ts.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js +297 -0
- package/dist/application/sync/outbound/effects/SyncEffectSupervisor.js.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts +100 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorker.d.ts.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorker.js +345 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorker.js.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts +46 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.d.ts.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js +63 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerConstants.js.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts +20 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.d.ts.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js +102 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerDispatch.js.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts +19 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.d.ts.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js +32 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerHelpers.js.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts +30 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.d.ts.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js +202 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerRouting.js.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts +17 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.d.ts.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js +80 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTiming.js.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts +13 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.d.ts.map +1 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js +205 -0
- package/dist/application/sync/outbound/effects/SyncEffectWorkerTransitions.js.map +1 -0
- package/dist/application/sync/outbound/projection/ProjectionEffectFactory.d.ts +81 -0
- package/dist/application/sync/outbound/projection/ProjectionEffectFactory.d.ts.map +1 -0
- package/dist/application/sync/outbound/projection/ProjectionEffectFactory.js +245 -0
- package/dist/application/sync/outbound/projection/ProjectionEffectFactory.js.map +1 -0
- package/dist/application/sync/outbound/reconciliation/ReconciliationScanner.d.ts +77 -0
- package/dist/application/sync/outbound/reconciliation/ReconciliationScanner.d.ts.map +1 -0
- package/dist/application/sync/outbound/reconciliation/ReconciliationScanner.js +518 -0
- package/dist/application/sync/outbound/reconciliation/ReconciliationScanner.js.map +1 -0
- package/dist/application/sync/telemetry/syncTiming.d.ts +50 -0
- package/dist/application/sync/telemetry/syncTiming.d.ts.map +1 -0
- package/dist/application/sync/telemetry/syncTiming.js +22 -0
- package/dist/application/sync/telemetry/syncTiming.js.map +1 -0
- package/dist/domain/conflict/errors.d.ts +42 -0
- package/dist/domain/conflict/errors.d.ts.map +1 -0
- package/dist/domain/conflict/errors.js +36 -0
- package/dist/domain/conflict/errors.js.map +1 -0
- package/dist/domain/conflict/index.d.ts +5 -0
- package/dist/domain/conflict/index.d.ts.map +1 -0
- package/dist/domain/conflict/index.js +3 -0
- package/dist/domain/conflict/index.js.map +1 -0
- package/dist/domain/conflict/transitions.d.ts +52 -0
- package/dist/domain/conflict/transitions.d.ts.map +1 -0
- package/dist/domain/conflict/transitions.js +115 -0
- package/dist/domain/conflict/transitions.js.map +1 -0
- package/dist/domain/errors/evaluation.d.ts +13 -0
- package/dist/domain/errors/evaluation.d.ts.map +1 -0
- package/dist/domain/errors/evaluation.js +14 -0
- package/dist/domain/errors/evaluation.js.map +1 -0
- package/dist/domain/errors/identity.d.ts +12 -0
- package/dist/domain/errors/identity.d.ts.map +1 -0
- package/dist/domain/errors/identity.js +15 -0
- package/dist/domain/errors/identity.js.map +1 -0
- package/dist/domain/errors/index.d.ts +7 -0
- package/dist/domain/errors/index.d.ts.map +1 -0
- package/dist/domain/errors/index.js +5 -0
- package/dist/domain/errors/index.js.map +1 -0
- package/dist/domain/errors/stableEncoding.d.ts +7 -0
- package/dist/domain/errors/stableEncoding.d.ts.map +1 -0
- package/dist/domain/errors/stableEncoding.js +8 -0
- package/dist/domain/errors/stableEncoding.js.map +1 -0
- package/dist/domain/errors/types.d.ts +20 -0
- package/dist/domain/errors/types.d.ts.map +1 -0
- package/dist/domain/errors/types.js +18 -0
- package/dist/domain/errors/types.js.map +1 -0
- package/dist/domain/evaluate/constants.d.ts +55 -0
- package/dist/domain/evaluate/constants.d.ts.map +1 -0
- package/dist/domain/evaluate/constants.js +41 -0
- package/dist/domain/evaluate/constants.js.map +1 -0
- package/dist/domain/evaluate/contracts.d.ts +125 -0
- package/dist/domain/evaluate/contracts.d.ts.map +1 -0
- package/dist/domain/evaluate/contracts.js +8 -0
- package/dist/domain/evaluate/contracts.js.map +1 -0
- package/dist/domain/evaluate/evaluateBatch.d.ts +11 -0
- package/dist/domain/evaluate/evaluateBatch.d.ts.map +1 -0
- package/dist/domain/evaluate/evaluateBatch.js +91 -0
- package/dist/domain/evaluate/evaluateBatch.js.map +1 -0
- package/dist/domain/evaluate/fieldEvaluation.d.ts +14 -0
- package/dist/domain/evaluate/fieldEvaluation.d.ts.map +1 -0
- package/dist/domain/evaluate/fieldEvaluation.js +133 -0
- package/dist/domain/evaluate/fieldEvaluation.js.map +1 -0
- package/dist/domain/evaluate/identity.d.ts +21 -0
- package/dist/domain/evaluate/identity.d.ts.map +1 -0
- package/dist/domain/evaluate/identity.js +72 -0
- package/dist/domain/evaluate/identity.js.map +1 -0
- package/dist/domain/evaluate/index.d.ts +4 -0
- package/dist/domain/evaluate/index.d.ts.map +1 -0
- package/dist/domain/evaluate/index.js +3 -0
- package/dist/domain/evaluate/index.js.map +1 -0
- package/dist/domain/evaluate/preconditions.d.ts +30 -0
- package/dist/domain/evaluate/preconditions.d.ts.map +1 -0
- package/dist/domain/evaluate/preconditions.js +389 -0
- package/dist/domain/evaluate/preconditions.js.map +1 -0
- package/dist/domain/evaluate/quarantine.d.ts +15 -0
- package/dist/domain/evaluate/quarantine.d.ts.map +1 -0
- package/dist/domain/evaluate/quarantine.js +185 -0
- package/dist/domain/evaluate/quarantine.js.map +1 -0
- package/dist/domain/index.d.ts +7 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +7 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/model/constants.d.ts +73 -0
- package/dist/domain/model/constants.d.ts.map +1 -0
- package/dist/domain/model/constants.js +59 -0
- package/dist/domain/model/constants.js.map +1 -0
- package/dist/domain/model/index.d.ts +3 -0
- package/dist/domain/model/index.d.ts.map +1 -0
- package/dist/domain/model/index.js +2 -0
- package/dist/domain/model/index.js.map +1 -0
- package/dist/domain/model/types.d.ts +330 -0
- package/dist/domain/model/types.d.ts.map +1 -0
- package/dist/domain/model/types.js +8 -0
- package/dist/domain/model/types.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/index.d.ts +3 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +3 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/dist/infrastructure/storage/constants.d.ts +4 -0
- package/dist/infrastructure/storage/constants.d.ts.map +1 -0
- package/dist/infrastructure/storage/constants.js +4 -0
- package/dist/infrastructure/storage/constants.js.map +1 -0
- package/dist/infrastructure/storage/errors.d.ts +55 -0
- package/dist/infrastructure/storage/errors.d.ts.map +1 -0
- package/dist/infrastructure/storage/errors.js +58 -0
- package/dist/infrastructure/storage/errors.js.map +1 -0
- package/dist/infrastructure/storage/index.d.ts +23 -0
- package/dist/infrastructure/storage/index.d.ts.map +1 -0
- package/dist/infrastructure/storage/index.js +13 -0
- package/dist/infrastructure/storage/index.js.map +1 -0
- package/dist/infrastructure/storage/recovery/restoreRecovery.d.ts +64 -0
- package/dist/infrastructure/storage/recovery/restoreRecovery.d.ts.map +1 -0
- package/dist/infrastructure/storage/recovery/restoreRecovery.js +148 -0
- package/dist/infrastructure/storage/recovery/restoreRecovery.js.map +1 -0
- package/dist/infrastructure/storage/sqlite/schema.d.ts +49 -0
- package/dist/infrastructure/storage/sqlite/schema.d.ts.map +1 -0
- package/dist/infrastructure/storage/sqlite/schema.js +529 -0
- package/dist/infrastructure/storage/sqlite/schema.js.map +1 -0
- package/dist/infrastructure/storage/sqlite/schemaTypes.d.ts +5 -0
- package/dist/infrastructure/storage/sqlite/schemaTypes.d.ts.map +1 -0
- package/dist/infrastructure/storage/sqlite/schemaTypes.js +2 -0
- package/dist/infrastructure/storage/sqlite/schemaTypes.js.map +1 -0
- package/dist/infrastructure/storage/sqlite/sqlScript.d.ts +9 -0
- package/dist/infrastructure/storage/sqlite/sqlScript.d.ts.map +1 -0
- package/dist/infrastructure/storage/sqlite/sqlScript.js +122 -0
- package/dist/infrastructure/storage/sqlite/sqlScript.js.map +1 -0
- package/dist/infrastructure/storage/sqlite/sqlState.d.ts +6 -0
- package/dist/infrastructure/storage/sqlite/sqlState.d.ts.map +1 -0
- package/dist/infrastructure/storage/sqlite/sqlState.js +14 -0
- package/dist/infrastructure/storage/sqlite/sqlState.js.map +1 -0
- package/dist/infrastructure/storage/sqlite/sqlTransaction.d.ts +6 -0
- package/dist/infrastructure/storage/sqlite/sqlTransaction.d.ts.map +1 -0
- package/dist/infrastructure/storage/sqlite/sqlTransaction.js +24 -0
- package/dist/infrastructure/storage/sqlite/sqlTransaction.js.map +1 -0
- package/dist/infrastructure/storage/sqlite/sqliteBridge.d.ts +44 -0
- package/dist/infrastructure/storage/sqlite/sqliteBridge.d.ts.map +1 -0
- package/dist/infrastructure/storage/sqlite/sqliteBridge.js +66 -0
- package/dist/infrastructure/storage/sqlite/sqliteBridge.js.map +1 -0
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts +101 -0
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.js +417 -0
- package/dist/infrastructure/storage/state/canonical/canonicalCommit.js.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationAudit.d.ts +16 -0
- package/dist/infrastructure/storage/state/observation/observationAudit.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationAudit.js +85 -0
- package/dist/infrastructure/storage/state/observation/observationAudit.js.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts +31 -0
- package/dist/infrastructure/storage/state/observation/observationCanonical.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationCanonical.js +439 -0
- package/dist/infrastructure/storage/state/observation/observationCanonical.js.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationConstants.d.ts +53 -0
- package/dist/infrastructure/storage/state/observation/observationConstants.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationConstants.js +41 -0
- package/dist/infrastructure/storage/state/observation/observationConstants.js.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationLedger.d.ts +49 -0
- package/dist/infrastructure/storage/state/observation/observationLedger.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationLedger.js +537 -0
- package/dist/infrastructure/storage/state/observation/observationLedger.js.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts +15 -0
- package/dist/infrastructure/storage/state/observation/observationQuarantine.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationQuarantine.js +100 -0
- package/dist/infrastructure/storage/state/observation/observationQuarantine.js.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationTypes.d.ts +124 -0
- package/dist/infrastructure/storage/state/observation/observationTypes.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationTypes.js +13 -0
- package/dist/infrastructure/storage/state/observation/observationTypes.js.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationValidation.d.ts +28 -0
- package/dist/infrastructure/storage/state/observation/observationValidation.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationValidation.js +223 -0
- package/dist/infrastructure/storage/state/observation/observationValidation.js.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationWriter.d.ts +28 -0
- package/dist/infrastructure/storage/state/observation/observationWriter.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/observation/observationWriter.js +293 -0
- package/dist/infrastructure/storage/state/observation/observationWriter.js.map +1 -0
- package/dist/infrastructure/storage/state/readonly/readOnlyObservation.d.ts +58 -0
- package/dist/infrastructure/storage/state/readonly/readOnlyObservation.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/readonly/readOnlyObservation.js +195 -0
- package/dist/infrastructure/storage/state/readonly/readOnlyObservation.js.map +1 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts +31 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.js +563 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriter.js.map +1 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts +118 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.js +18 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterContracts.js.map +1 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts +23 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js +84 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterHelpers.js.map +1 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts +16 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.d.ts.map +1 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js +97 -0
- package/dist/infrastructure/storage/state/resolution/resolutionWriterSql.js.map +1 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts +137 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.d.ts.map +1 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.js +396 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutbox.js.map +1 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts +101 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.d.ts.map +1 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js +9 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxContracts.js.map +1 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts +21 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.d.ts.map +1 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js +184 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSql.js.map +1 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts +25 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.d.ts.map +1 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js +158 -0
- package/dist/infrastructure/storage/sync/outbound/effectOutboxSupport.js.map +1 -0
- package/dist/infrastructure/storage/sync/shared/syncRegistry.d.ts +69 -0
- package/dist/infrastructure/storage/sync/shared/syncRegistry.d.ts.map +1 -0
- package/dist/infrastructure/storage/sync/shared/syncRegistry.js +272 -0
- package/dist/infrastructure/storage/sync/shared/syncRegistry.js.map +1 -0
- package/dist/infrastructure/storage/sync/shared/writerLease.d.ts +90 -0
- package/dist/infrastructure/storage/sync/shared/writerLease.d.ts.map +1 -0
- package/dist/infrastructure/storage/sync/shared/writerLease.js +282 -0
- package/dist/infrastructure/storage/sync/shared/writerLease.js.map +1 -0
- package/dist/shared/constants.d.ts +8 -0
- package/dist/shared/constants.d.ts.map +1 -0
- package/dist/shared/constants.js +8 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/encoding/constants.d.ts +45 -0
- package/dist/shared/encoding/constants.d.ts.map +1 -0
- package/dist/shared/encoding/constants.js +37 -0
- package/dist/shared/encoding/constants.js.map +1 -0
- package/dist/shared/encoding/index.d.ts +6 -0
- package/dist/shared/encoding/index.d.ts.map +1 -0
- package/dist/shared/encoding/index.js +4 -0
- package/dist/shared/encoding/index.js.map +1 -0
- package/dist/shared/encoding/stableEncode.d.ts +34 -0
- package/dist/shared/encoding/stableEncode.d.ts.map +1 -0
- package/dist/shared/encoding/stableEncode.js +210 -0
- package/dist/shared/encoding/stableEncode.js.map +1 -0
- package/dist/shared/encoding/typeGuards.d.ts +15 -0
- package/dist/shared/encoding/typeGuards.d.ts.map +1 -0
- package/dist/shared/encoding/typeGuards.js +11 -0
- package/dist/shared/encoding/typeGuards.js.map +1 -0
- package/dist/shared/encoding/types.d.ts +52 -0
- package/dist/shared/encoding/types.d.ts.map +1 -0
- package/dist/shared/encoding/types.js +8 -0
- package/dist/shared/encoding/types.js.map +1 -0
- package/dist/shared/index.d.ts +6 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +6 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/state/constants.d.ts +16 -0
- package/dist/shared/state/constants.d.ts.map +1 -0
- package/dist/shared/state/constants.js +16 -0
- package/dist/shared/state/constants.js.map +1 -0
- package/dist/shared/state/index.d.ts +3 -0
- package/dist/shared/state/index.d.ts.map +1 -0
- package/dist/shared/state/index.js +2 -0
- package/dist/shared/state/index.js.map +1 -0
- package/dist/shared/state/types.d.ts +23 -0
- package/dist/shared/state/types.d.ts.map +1 -0
- package/dist/shared/state/types.js +2 -0
- package/dist/shared/state/types.js.map +1 -0
- package/dist/shared/validation.d.ts +9 -0
- package/dist/shared/validation.d.ts.map +1 -0
- package/dist/shared/validation.js +25 -0
- package/dist/shared/validation.js.map +1 -0
- package/docs/architecture.md +72 -0
- package/docs/code-guidelines.md +256 -0
- package/docs/development.md +48 -0
- package/docs/git-workflow.md +224 -0
- package/docs/mikro-orm-adapter-spike.md +248 -0
- package/docs/quick-start.md +102 -0
- package/docs/sql-layer-plan.md +59 -0
- package/docs/sync-bulk-write-benchmark.md +1036 -0
- package/docs/sync-observability.md +71 -0
- package/docs/task-queue-write-model.md +640 -0
- package/docs/typed-sheets-mvp-scope-2026-06-29.md +490 -0
- package/docs/typed-sheets-plan.md +417 -0
- package/docs/write-and-synchronization-flow.md +71 -0
- package/package.json +68 -0
|
@@ -0,0 +1,1036 @@
|
|
|
1
|
+
# Sync bulk-write benchmark
|
|
2
|
+
|
|
3
|
+
## 2026-07-24 — raw Apps Script write
|
|
4
|
+
|
|
5
|
+
- Branch: `benchmark/apps-script-bulk-write`
|
|
6
|
+
- Script: `apps-script/gateway/BulkWriteBenchmark.gs`
|
|
7
|
+
- Entry point: `runBulkWriteBenchmark100()`
|
|
8
|
+
- Backend: Apps Script `SpreadsheetApp`
|
|
9
|
+
- Target: `__typed_sheets_bulk_write_benchmark`
|
|
10
|
+
- Dataset: 100 rows × 6 columns = 600 cells
|
|
11
|
+
- Write mode: one contiguous `setValues()` followed by one `flush()`
|
|
12
|
+
- Setup: the benchmark appended at row 22; one-time sheet setup was excluded
|
|
13
|
+
from the measured write interval
|
|
14
|
+
|
|
15
|
+
| Measurement | Setup | `setValues()` | `flush()` | Steady-state total |
|
|
16
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
17
|
+
| 100 rows / 600 cells | 713 ms | 96 ms | 278 ms | 374 ms |
|
|
18
|
+
|
|
19
|
+
Throughput was 267.38 rows/second and 1,604.28 cells/second.
|
|
20
|
+
|
|
21
|
+
## Comparison: full sync request
|
|
22
|
+
|
|
23
|
+
The previous clean-DB test sent 20 customer effects through the complete
|
|
24
|
+
Gateway path with `POST /sync/once?maxEffects=20`. It took 75,409 ms and all 20
|
|
25
|
+
effects were applied. That path included snapshot reads, row metadata, CAS
|
|
26
|
+
checks, postcondition reads, receipts, and request tracing.
|
|
27
|
+
|
|
28
|
+
The raw write benchmark is therefore roughly 1,000 times faster per row than
|
|
29
|
+
the complete 20-effect sync request. This is not a correctness comparison: the
|
|
30
|
+
benchmark intentionally bypasses concurrency checks, user-edit detection,
|
|
31
|
+
receipts, and postcondition verification.
|
|
32
|
+
|
|
33
|
+
## 2026-07-24 — isolated stage benchmark
|
|
34
|
+
|
|
35
|
+
- Branch: `benchmark/apps-script-bulk-write`
|
|
36
|
+
- Script: `apps-script/gateway/BulkWriteBenchmark.gs`
|
|
37
|
+
- Entry point: `runBulkWriteStageBenchmark20()`
|
|
38
|
+
- Backend: Apps Script `SpreadsheetApp`
|
|
39
|
+
- Dataset: 20 rows × 6 columns
|
|
40
|
+
- Setup time is excluded from each stage measurement.
|
|
41
|
+
|
|
42
|
+
| Stage | Setup | Operation | Flush | Measured total |
|
|
43
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
44
|
+
| `metadata_read` | 35,117 ms | 3,609 ms | — | 3,609 ms |
|
|
45
|
+
| `metadata_write` | 33,273 ms | 31,637 ms | 59 ms | 31,696 ms |
|
|
46
|
+
| `snapshot_read` | 19,540 ms | 4,303 ms | — | 4,303 ms |
|
|
47
|
+
| `cas_compare` | 0 ms | 2 ms | — | 2 ms |
|
|
48
|
+
| `postcondition_read` | 18,280 ms | 2,646 ms | — | 2,646 ms |
|
|
49
|
+
| `receipt_read` | 1,200 ms | 119 ms | — | 119 ms |
|
|
50
|
+
| `receipt_write` | 1,597 ms | 430 ms | 352 ms | 782 ms |
|
|
51
|
+
|
|
52
|
+
The dominant measured stage is `metadata_write`: rewriting three row metadata
|
|
53
|
+
entries for 20 rows took about 31.7 seconds, while the `flush()` itself took
|
|
54
|
+
only 59 ms. This identifies the per-row Developer Metadata API calls as the
|
|
55
|
+
primary bottleneck. The in-memory CAS comparison and receipt operations are
|
|
56
|
+
not significant for this test size.
|
|
57
|
+
|
|
58
|
+
## 2026-07-24 — visible-state metadata migration
|
|
59
|
+
|
|
60
|
+
The Gateway batch path now treats SQLite as the authority for
|
|
61
|
+
`visibleRevision` (가시적 버전) and `visibleHash` (가시적 상태 해시):
|
|
62
|
+
|
|
63
|
+
- Sheet Developer Metadata keeps only the projection-local row anchor.
|
|
64
|
+
- Batch CAS compares the bulk-read cell hash with the effect's
|
|
65
|
+
`expectedVisibleHash` from SQLite; it no longer reads or rewrites a
|
|
66
|
+
revision/hash metadata pair for every changed row.
|
|
67
|
+
- Successful results and receipts derive the next revision as
|
|
68
|
+
`expectedVisibleRevision + 1`.
|
|
69
|
+
- Batch postconditions verify the raw cell hash in one range read.
|
|
70
|
+
- The receipt sheet remains because it is durable response-loss evidence, not
|
|
71
|
+
per-row Developer Metadata.
|
|
72
|
+
- Existing visible revision/hash metadata is not deleted; it is left inert so
|
|
73
|
+
this migration does not perform a destructive cleanup of user sheets.
|
|
74
|
+
|
|
75
|
+
This change is source-level and requires deploying the updated
|
|
76
|
+
`apps-script/gateway/Code.gs` before a live benchmark. The next comparison
|
|
77
|
+
should rerun `runBulkWriteStageBenchmark20()` and a 20-effect sync against a
|
|
78
|
+
fresh test sheet, then verify that `metadata_write` no longer appears in the
|
|
79
|
+
production batch path and that SQLite confirmations still advance normally.
|
|
80
|
+
|
|
81
|
+
## 2026-07-24 — new Gateway progressive load test
|
|
82
|
+
|
|
83
|
+
- Branch: `benchmark/apps-script-bulk-write`
|
|
84
|
+
- Harness: `.local/lib-test-beta-0.2.0-beta1`
|
|
85
|
+
- Backend: MikroORM + SQLite locally, deployed Apps Script Gateway remotely
|
|
86
|
+
- Database: fresh `typed-sheets-load-test-new-gateway.sqlite`
|
|
87
|
+
- Setup: provisioned five projection tabs and seeded 20 customers plus 20
|
|
88
|
+
products; the 40-effect seed sync took 46.7 seconds and applied all effects
|
|
89
|
+
with no failures. Setup is excluded from the stage comparison below.
|
|
90
|
+
- Gateway behavior: visible revision/hash row metadata disabled; anchor
|
|
91
|
+
metadata and receipt sheet retained.
|
|
92
|
+
- Stage runner: the local Node fetch runner created orders in groups of
|
|
93
|
+
`1/5/10/20` and called `/sync/once?maxEffects=1/5/10/20` once after each
|
|
94
|
+
group. Each order produced four effects in this scenario.
|
|
95
|
+
|
|
96
|
+
| Orders added | `maxEffects` | Order writes | Sync request | Applied | Failed | Pending after stage |
|
|
97
|
+
| ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
98
|
+
| 1 | 1 | 6.03 s | 5.97 s | 1 | 0 | 3 |
|
|
99
|
+
| 5 | 5 | 11.07 s | 11.03 s | 5 | 0 | 18 |
|
|
100
|
+
| 10 | 10 | 16.22 s | 16.15 s | 10 | 0 | 48 |
|
|
101
|
+
| 20 | 20 | 23.24 s | 23.10 s | 20 | 0 | 108 |
|
|
102
|
+
|
|
103
|
+
The stage sync cost was approximately 1.15 seconds per effect at the 20-effect
|
|
104
|
+
batch, compared with 5.97 seconds per effect at the single-effect batch. The
|
|
105
|
+
cost is still substantial, but the 20-effect request is about 3.3× faster than
|
|
106
|
+
the earlier 75.4-second 20-effect run that rewrote visible row metadata.
|
|
107
|
+
|
|
108
|
+
The accumulated 108-effect backlog was then drained with
|
|
109
|
+
`/sync/once?maxEffects=100`:
|
|
110
|
+
|
|
111
|
+
| Drain pass | Duration | Selected | Applied | Deferred | Failed | Pending after pass |
|
|
112
|
+
| ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
113
|
+
| 1 | 59.85 s | 92 | 60 | 32 | 0 | 48 |
|
|
114
|
+
| 2 | 69.49 s | 48 | 48 | 0 | 0 | 0 |
|
|
115
|
+
|
|
116
|
+
Final SQLite counts were 36 orders, 36 order items, and 36 payments. The final
|
|
117
|
+
outbox contained 184 applied effects and no failed effects. This confirms that
|
|
118
|
+
the new Gateway path can drain this clean backlog, but it does not yet prove
|
|
119
|
+
that arbitrary response loss or a manually edited Sheet will always converge.
|
|
120
|
+
|
|
121
|
+
## 2026-07-24 — write-batch read reduction
|
|
122
|
+
|
|
123
|
+
- Branch: `benchmark/apps-script-bulk-write`
|
|
124
|
+
- Source: `apps-script/gateway/Code.gs`
|
|
125
|
+
- Scope: the `applyEffects` write path only; the standalone `readSnapshot` path
|
|
126
|
+
remains unchanged
|
|
127
|
+
|
|
128
|
+
The write path no longer uses the reader-oriented full snapshot at both sides
|
|
129
|
+
of a batch:
|
|
130
|
+
|
|
131
|
+
- `createSyncBatchContext_` now reads one raw-value range and row anchors using
|
|
132
|
+
`readSyncBatchState_`; it does not read formulas, display values, or merged
|
|
133
|
+
ranges, and it does not repeat the raw-value range read.
|
|
134
|
+
- The postcondition check now reads only the changed rows, grouped into
|
|
135
|
+
contiguous ranges, instead of reading every registered row from row 2 to the
|
|
136
|
+
sheet's last row.
|
|
137
|
+
- The final full snapshot read was removed from the write response. SQLite is
|
|
138
|
+
authoritative for visible revision/hash state, while each changed effect is
|
|
139
|
+
still verified from raw Sheet cells before its receipt is written. The
|
|
140
|
+
optional `snapshotHash` in an apply response is therefore `null`.
|
|
141
|
+
- Stable row anchors and the receipt sheet remain unchanged because they are
|
|
142
|
+
still required for row identity and response-loss recovery.
|
|
143
|
+
|
|
144
|
+
Static verification passed with `node --check`, the focused Apps Script source
|
|
145
|
+
and sync-client tests (9 tests), `npm run typecheck`, and `npm run build`. A live
|
|
146
|
+
comparison requires deploying this `Code.gs` first. The next benchmark should
|
|
147
|
+
compare the `prepare_batch_context`, `batch_flush`, and postcondition phase
|
|
148
|
+
durations against the progressive run above, especially on a sheet with many
|
|
149
|
+
existing rows.
|
|
150
|
+
|
|
151
|
+
## 2026-07-24 — fresh-sheet progressive load test after a new deployment
|
|
152
|
+
|
|
153
|
+
- Branch: `benchmark/apps-script-bulk-write`
|
|
154
|
+
- Harness: `.local/lib-test-beta-0.2.0-beta1`
|
|
155
|
+
- Backend: MikroORM + SQLite locally, newly deployed Apps Script Gateway
|
|
156
|
+
- Database: fresh `data/typed-sheets-load-test-progressive-20260724.sqlite`
|
|
157
|
+
- Dataset: 20 customers and 20 products seeded into a new spreadsheet; seed
|
|
158
|
+
synchronization was 40 effects and is excluded from the progressive stage
|
|
159
|
+
comparison
|
|
160
|
+
- Setup commands: `POST /sync/provision`, `POST /load-test/seed` with
|
|
161
|
+
`customers=20`, `products=20`, then `POST /sync/once?maxEffects=100`
|
|
162
|
+
- Progressive script: create order groups of `1/5/10/20`; after each group,
|
|
163
|
+
call `POST /sync/once?maxEffects=<group size>` and record `/metrics` before
|
|
164
|
+
and after the call
|
|
165
|
+
- Background worker: disabled; synchronization was explicitly invoked by the
|
|
166
|
+
test runner
|
|
167
|
+
|
|
168
|
+
The 40-effect seed took 49.87 seconds and applied all effects without failure.
|
|
169
|
+
|
|
170
|
+
| Orders added | `maxEffects` | Order writes | Sync request | Applied | Failed | Pending after stage |
|
|
171
|
+
| ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
172
|
+
| 1 | 1 | 21.98 ms | 6.40 s | 1 | 0 | 3 |
|
|
173
|
+
| 5 | 5 | 42.29 ms | 9.13 s | 5 | 0 | 18 |
|
|
174
|
+
| 10 | 10 | 77.42 ms | 14.72 s | 10 | 0 | 48 |
|
|
175
|
+
| 20 | 20 | 139.47 ms | 27.39 s | 20 | 0 | 108 |
|
|
176
|
+
|
|
177
|
+
The first drain pass selected 92 effects, applied 60, deferred 32 because of
|
|
178
|
+
the Gateway prefix limit, and took 56.62 seconds. The following 16-effect
|
|
179
|
+
`load-payments-state` request exceeded the 120-second client timeout. Its
|
|
180
|
+
response-loss recovery then issued one full `readSnapshot` request per unknown
|
|
181
|
+
effect, with individual reads taking roughly 7–16 seconds. The observed state
|
|
182
|
+
after that recovery was 162 applied, 16 failed, and 6 pending; every failed
|
|
183
|
+
effect had `postcondition_unavailable`.
|
|
184
|
+
|
|
185
|
+
A follow-up drain selected 21 effects (the 16 failed effects were eligible for
|
|
186
|
+
retry plus 5 pending effects), took 184.39 seconds, applied only the 5 pending
|
|
187
|
+
effects, and left the same 16 failures. Final metrics were:
|
|
188
|
+
|
|
189
|
+
| SQLite table | Count |
|
|
190
|
+
| --- | ---: |
|
|
191
|
+
| customers | 20 |
|
|
192
|
+
| products | 20 |
|
|
193
|
+
| orders | 36 |
|
|
194
|
+
| orderItems | 36 |
|
|
195
|
+
| payments | 36 |
|
|
196
|
+
|
|
197
|
+
The final outbox contained 167 applied, 16 failed, and 1 pending effect. This
|
|
198
|
+
test confirms the dominant failure path is not raw `setValues()` throughput:
|
|
199
|
+
an oversized or slow payment batch reaches the 120-second transport timeout,
|
|
200
|
+
then failed-effect retry multiplies the cost by performing full snapshot reads
|
|
201
|
+
per effect. The next fix should make postcondition recovery batch-aware and
|
|
202
|
+
avoid retrying all failed effects in the normal drain pass without a bounded
|
|
203
|
+
backoff or explicit recovery mode.
|
|
204
|
+
|
|
205
|
+
## 2026-07-24 — fresh-sheet progressive load test with batch postcondition recovery
|
|
206
|
+
|
|
207
|
+
- Branch: `benchmark/apps-script-bulk-write`
|
|
208
|
+
- Harness: `.local/lib-test-beta-0.2.0-beta1`
|
|
209
|
+
- Backend: MikroORM + SQLite locally, newly deployed Apps Script Gateway
|
|
210
|
+
- Database: fresh `data/typed-sheets-load-test-progressive-20260724-new-sheet.sqlite`
|
|
211
|
+
- Dataset: 20 customers and 20 products seeded into a fresh spreadsheet; seed
|
|
212
|
+
synchronization applied 40 effects without failure and is excluded from the
|
|
213
|
+
progressive stage comparison
|
|
214
|
+
- Background worker: disabled; synchronization was explicitly invoked with
|
|
215
|
+
`POST /sync/once`
|
|
216
|
+
- Recovery path: `readEffectPostconditions` batch API; no per-effect full
|
|
217
|
+
`readSnapshot` recovery call
|
|
218
|
+
|
|
219
|
+
| Orders added | `maxEffects` | Order writes | Sync request | Selected | Applied | Requeued | Failed | Pending after stage |
|
|
220
|
+
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
221
|
+
| 1 | 1 | 57.16 ms | 5.96 s | 1 | 1 | 0 | 0 | 3 |
|
|
222
|
+
| 5 | 5 | 44.42 ms | 125.51 s | 5 | 0 | 5 | 0 | 23 |
|
|
223
|
+
| 10 | 10 | 77.86 ms | 15.38 s | 10 | 10 | 0 | 0 | 53 |
|
|
224
|
+
| 20 | 20 | 558.76 ms | 18.54 s | 20 | 20 | 0 | 0 | 113 |
|
|
225
|
+
|
|
226
|
+
The five-effect stage exceeded the 120-second client timeout, but the new
|
|
227
|
+
batch postcondition recovery classified all five effects as `unapplied` and
|
|
228
|
+
returned them to `pending`; it did not create `postcondition_unavailable`
|
|
229
|
+
failures or issue one full snapshot read per effect. The other stages applied
|
|
230
|
+
normally.
|
|
231
|
+
|
|
232
|
+
The backlog was drained in multiple passes because the Apps Script Gateway
|
|
233
|
+
still limits each apply prefix to 20 effects, and product stock effects for the
|
|
234
|
+
same target must respect predecessor ordering:
|
|
235
|
+
|
|
236
|
+
| Drain pass | Selected | Applied | Deferred | Pending after pass |
|
|
237
|
+
| ---: | ---: | ---: | ---: | ---: |
|
|
238
|
+
| 1 | 78 | 46 | 32 | 67 |
|
|
239
|
+
| 2 | 33 | 33 | 0 | 34 |
|
|
240
|
+
| 3 | 1 | 1 | 0 | 33 |
|
|
241
|
+
| Ordered-chain passes | 25 | 25 | 0 | 0 |
|
|
242
|
+
|
|
243
|
+
Final metrics were 184 applied effects, zero failed effects, and zero pending
|
|
244
|
+
effects. SQLite contained 36 orders, 36 order items, and 36 payments. This
|
|
245
|
+
reproduces the variable Gateway latency, but confirms that batch postcondition
|
|
246
|
+
recovery prevents the previous per-effect snapshot amplification and allows
|
|
247
|
+
the backlog to converge to zero.
|
|
248
|
+
|
|
249
|
+
## 2026-07-25 — published beta progressive API load test
|
|
250
|
+
|
|
251
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
252
|
+
- Harness: `.local/lib-test-beta-0.2.0-beta1`
|
|
253
|
+
- Package: published `typed-sheets@0.2.0-beta-1` (not a local workspace link)
|
|
254
|
+
- Backend: MikroORM + SQLite locally, deployed Apps Script Gateway remotely
|
|
255
|
+
- Gateway configuration: the new untracked `.env` values supplied for this
|
|
256
|
+
test; the secret is intentionally not recorded here
|
|
257
|
+
- Background worker: disabled; the load runner invoked `/sync/once` itself
|
|
258
|
+
- Existing state: the test DB already contained one customer, one product, and
|
|
259
|
+
two applied seed effects; the spreadsheet was also reused, so this is not a
|
|
260
|
+
clean-sheet comparison
|
|
261
|
+
- Commands used:
|
|
262
|
+
|
|
263
|
+
```sh
|
|
264
|
+
LOAD_TEST_DURATION_MS=5000 LOAD_TEST_CONCURRENCY=1 \
|
|
265
|
+
LOAD_TEST_WRITE_RATIO=0.7 LOAD_TEST_SEED_CUSTOMERS=5 \
|
|
266
|
+
LOAD_TEST_SEED_PRODUCTS=5 LOAD_TEST_SYNC=1 LOAD_TEST_DRAIN=0 \
|
|
267
|
+
node --env-file-if-exists=.env load-test.mjs
|
|
268
|
+
|
|
269
|
+
LOAD_TEST_DURATION_MS=10000 LOAD_TEST_CONCURRENCY=2 \
|
|
270
|
+
LOAD_TEST_WRITE_RATIO=0.7 LOAD_TEST_SEED_CUSTOMERS=5 \
|
|
271
|
+
LOAD_TEST_SEED_PRODUCTS=5 LOAD_TEST_SYNC=1 LOAD_TEST_DRAIN=0 \
|
|
272
|
+
node --env-file-if-exists=.env load-test.mjs
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
| Stage | Duration | Concurrency | Generated orders | Total requests | Requests/s | Sync calls recorded | Applied | Pending | Processing | Failed |
|
|
276
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
277
|
+
| Low | 5 s | 1 | 643 | 918 | 181.30 | 0 | 2 | 3,558 | 10 | 0 |
|
|
278
|
+
| Medium | 10 s | 2 | 876 | 1,283 | 127.24 | 0 | 18 | 8,429 | 45 | 0 |
|
|
279
|
+
|
|
280
|
+
The `/orders` route combines order writes and order-list reads, so its latency
|
|
281
|
+
is not a write-only measurement. The low-stage mixed route latency was p50
|
|
282
|
+
6.18 ms / p95 9.74 ms / p99 13.93 ms; the medium-stage latency was p50
|
|
283
|
+
14.67 ms / p95 28.77 ms / p99 39.45 ms. All HTTP requests succeeded.
|
|
284
|
+
|
|
285
|
+
The load runner did not receive a completed `maxEffects=50` sync response before
|
|
286
|
+
each short stage ended, so those calls were aborted from the runner's point of
|
|
287
|
+
view. A standalone `maxEffects=1` request after the low stage selected and
|
|
288
|
+
applied one effect successfully in roughly 3.3 seconds. This gives a direct
|
|
289
|
+
steady-state reference for the consumer path, excluding the load runner's
|
|
290
|
+
health and seed setup.
|
|
291
|
+
|
|
292
|
+
The medium stage already saturated the projection path: local SQLite produced
|
|
293
|
+
orders much faster than the Gateway consumed effects, growing the backlog from
|
|
294
|
+
3,558 to 8,429 pending effects while failed effects remained at zero. The high
|
|
295
|
+
stage was intentionally not run because it would add backlog and external
|
|
296
|
+
Gateway traffic without changing this conclusion. A clean-sheet drain test is
|
|
297
|
+
still required separately.
|
|
298
|
+
|
|
299
|
+
## 2026-07-25 — new Gateway fast-append and management-loop smoke test
|
|
300
|
+
|
|
301
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
302
|
+
- Harness: `.local/lib-test-beta-0.2.0-beta1`
|
|
303
|
+
- Backend: MikroORM + SQLite locally, newly deployed Apps Script Gateway
|
|
304
|
+
- Database: fresh `data/typed-sheets-load-test-20260725-new-gateway.sqlite`
|
|
305
|
+
- Gateway configuration: new untracked `.env` values supplied for this test;
|
|
306
|
+
the secret is intentionally not recorded here
|
|
307
|
+
- Background worker: enabled; reconciliation(불일치 보정) management loop was
|
|
308
|
+
enabled with its default 60-second interval
|
|
309
|
+
- Command:
|
|
310
|
+
|
|
311
|
+
```sh
|
|
312
|
+
env LOAD_TEST_PROVISION=1 LOAD_TEST_DURATION_MS=3000 \
|
|
313
|
+
LOAD_TEST_CONCURRENCY=1 LOAD_TEST_WRITE_RATIO=1 \
|
|
314
|
+
LOAD_TEST_SEED_CUSTOMERS=20 LOAD_TEST_SEED_PRODUCTS=20 \
|
|
315
|
+
LOAD_TEST_DRAIN=1 LOAD_TEST_SYNC_DRAIN_TIMEOUT_MS=120000 \
|
|
316
|
+
npm run load-test
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
| Stage | Setup / provision | Traffic duration | Generated orders | HTTP errors | Applied | Pending | Processing | Failed |
|
|
320
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
321
|
+
| 20/20 seed | 13.24 s | 3 s | 435 | 0 | 300 | 2,178 | 100 | 0 |
|
|
322
|
+
|
|
323
|
+
The local API successfully created 435 orders, 834 order items, and 435
|
|
324
|
+
payments; all 562 HTTP requests succeeded. The Gateway accepted
|
|
325
|
+
`fastAppendRows` batches of 20, 80, and 100 rows, and each returned `applied`,
|
|
326
|
+
but the remote requests still took approximately 16–37 seconds each. The
|
|
327
|
+
SQLite producer therefore outpaced the Gateway consumer and the drain did not
|
|
328
|
+
reach zero within 120 seconds.
|
|
329
|
+
|
|
330
|
+
The management loop exposed a compatibility defect after provisioning:
|
|
331
|
+
`readSnapshot` returned a protocol version that the Node client rejected as
|
|
332
|
+
unsupported. Consequently reconciliation did not run, so this result validated
|
|
333
|
+
fast append only and did not validate automatic drift repair. The protocol was
|
|
334
|
+
aligned in the follow-up run below.
|
|
335
|
+
|
|
336
|
+
## 2026-07-26 — protocol-aligned Gateway load test
|
|
337
|
+
|
|
338
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
339
|
+
- Harness: `.local/lib-test-beta-0.2.0-beta1`
|
|
340
|
+
- Backend: MikroORM + SQLite locally, deployed Apps Script Gateway remotely
|
|
341
|
+
- Database: fresh `/private/tmp/typed-sheets-protocol-fixed-20260726.sqlite`
|
|
342
|
+
- Gateway configuration: current untracked `.env` values supplied for this
|
|
343
|
+
test; the secret is intentionally not recorded here
|
|
344
|
+
- Background worker: enabled; reconciliation(불일치 보정) management loop was
|
|
345
|
+
enabled
|
|
346
|
+
- The SQLite database was fresh, but the remote spreadsheet was reused and was
|
|
347
|
+
not an empty-sheet comparison. The first reconciliation scan observed
|
|
348
|
+
existing remote rows.
|
|
349
|
+
- Server command:
|
|
350
|
+
|
|
351
|
+
```sh
|
|
352
|
+
TYPED_SHEETS_DB_PATH=/private/tmp/typed-sheets-protocol-fixed-20260726.sqlite \
|
|
353
|
+
TYPED_SHEETS_API_PORT=3200 TYPED_SHEETS_BACKGROUND_WORKER=1 npm start
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
- Load command:
|
|
357
|
+
|
|
358
|
+
```sh
|
|
359
|
+
LOAD_TEST_PROVISION=1 LOAD_TEST_DURATION_MS=3000 \
|
|
360
|
+
LOAD_TEST_CONCURRENCY=1 LOAD_TEST_WRITE_RATIO=1 \
|
|
361
|
+
LOAD_TEST_SEED_CUSTOMERS=20 LOAD_TEST_SEED_PRODUCTS=20 \
|
|
362
|
+
LOAD_TEST_DRAIN=1 LOAD_TEST_SYNC_DRAIN_TIMEOUT_MS=120000 \
|
|
363
|
+
npm run load-test
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
| Metric | Result |
|
|
367
|
+
| --- | ---: |
|
|
368
|
+
| Provision setup | 7.49 s |
|
|
369
|
+
| Traffic duration | 3 s |
|
|
370
|
+
| Generated orders | 465 |
|
|
371
|
+
| Total HTTP requests | 591 |
|
|
372
|
+
| HTTP errors | 0 |
|
|
373
|
+
| Steady-state `/orders` requests | 465 successful; p50 6.21 ms / p95 8.93 ms / p99 12.53 ms |
|
|
374
|
+
| Total elapsed including drain wait | 130.99 s |
|
|
375
|
+
| Final applied effects | 20 |
|
|
376
|
+
| Final pending effects | 3,605 |
|
|
377
|
+
| Final failed effects | 0 |
|
|
378
|
+
|
|
379
|
+
The protocol error disappeared: every observed `readSnapshot` request returned
|
|
380
|
+
`ok: true`, including reconciliation reads. However, the first reconciliation
|
|
381
|
+
scan on the reused sheet found 1,350 desired rows and 500 scanned remote rows,
|
|
382
|
+
then enqueued 885 correction effects. Those corrections competed with the
|
|
383
|
+
load-test writes. The Gateway applied the first 20-row `fastAppendRows` batch,
|
|
384
|
+
but returned `operation_failed` for later 80-row and 100-row batches; the
|
|
385
|
+
worker requeued those effects and no final `failed` rows remained. This run
|
|
386
|
+
therefore confirms protocol compatibility, but not successful convergence.
|
|
387
|
+
|
|
388
|
+
The result also shows that starting reconciliation immediately against a
|
|
389
|
+
non-empty reused sheet contaminates a producer-throughput test. The next
|
|
390
|
+
benchmark should use a clean spreadsheet, disable reconciliation for the raw
|
|
391
|
+
fast-append measurement, then run reconciliation separately with a controlled
|
|
392
|
+
drift case.
|
|
393
|
+
|
|
394
|
+
## 2026-07-26 — clean Sheet protocol-aligned load test
|
|
395
|
+
|
|
396
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
397
|
+
- Harness: `.local/lib-test-beta-0.2.0-beta1`
|
|
398
|
+
- Backend: MikroORM + SQLite locally, newly deployed Apps Script Gateway
|
|
399
|
+
- Database: fresh `/private/tmp/typed-sheets-new-sheet-20260726.sqlite`
|
|
400
|
+
- Gateway configuration: new untracked `.env` values supplied for this test;
|
|
401
|
+
the secret is intentionally not recorded here
|
|
402
|
+
- Background worker: enabled; reconciliation(불일치 보정) management loop was
|
|
403
|
+
enabled
|
|
404
|
+
- The remote Sheet was new and passed provisioning successfully.
|
|
405
|
+
- Command:
|
|
406
|
+
|
|
407
|
+
```sh
|
|
408
|
+
LOAD_TEST_PROVISION=1 LOAD_TEST_DURATION_MS=3000 \
|
|
409
|
+
LOAD_TEST_CONCURRENCY=1 LOAD_TEST_WRITE_RATIO=1 \
|
|
410
|
+
LOAD_TEST_SEED_CUSTOMERS=20 LOAD_TEST_SEED_PRODUCTS=20 \
|
|
411
|
+
LOAD_TEST_DRAIN=1 LOAD_TEST_SYNC_DRAIN_TIMEOUT_MS=120000 \
|
|
412
|
+
npm run load-test
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
| Metric | Result |
|
|
416
|
+
| --- | ---: |
|
|
417
|
+
| Provision setup | 16.89 s |
|
|
418
|
+
| Traffic duration | 3 s |
|
|
419
|
+
| Generated orders | 393 |
|
|
420
|
+
| Generated order items | 759 |
|
|
421
|
+
| Generated payments | 393 |
|
|
422
|
+
| Total HTTP requests | 519 |
|
|
423
|
+
| HTTP errors | 0 |
|
|
424
|
+
| Steady-state `/orders` requests | 393 successful; p50 7.29 ms / p95 11.26 ms / p99 15.95 ms |
|
|
425
|
+
| Total elapsed including drain wait | 140.32 s |
|
|
426
|
+
| Final applied effects | 100 |
|
|
427
|
+
| Final pending effects | 2,164 |
|
|
428
|
+
| Final processing effects | 100 |
|
|
429
|
+
| Final failed effects | 0 |
|
|
430
|
+
|
|
431
|
+
The clean Sheet removed the previous reused-data problem for the initial
|
|
432
|
+
fast-append calls: 20-row and 80-row `fastAppendRows` batches were both
|
|
433
|
+
applied successfully, and no protocol or `operation_failed` error was observed
|
|
434
|
+
during those batches. However, the management loop later saw the canonical
|
|
435
|
+
SQLite state growing faster than the Sheet snapshot and enqueued additional
|
|
436
|
+
reconciliation work. The worker could not drain the resulting queue within
|
|
437
|
+
120 seconds; 2,164 effects remained pending and 100 remained processing when
|
|
438
|
+
the load runner stopped. A separate 20-effect correction request observed an
|
|
439
|
+
`operation_failed` response while the server was being shut down, so that
|
|
440
|
+
late response should not be treated as a clean fast-append result.
|
|
441
|
+
|
|
442
|
+
This isolates the current conclusion more clearly: a clean Sheet can accept
|
|
443
|
+
the tested 20- and 80-row fast-append batches, but the combined producer plus
|
|
444
|
+
reconciliation workload still exceeds the worker/Gateway drain rate. A raw
|
|
445
|
+
fast-append benchmark with reconciliation disabled is still needed to measure
|
|
446
|
+
the Gateway ceiling without management-loop work competing for the same worker.
|
|
447
|
+
|
|
448
|
+
## 2026-07-26 — new Gateway progressive load test
|
|
449
|
+
|
|
450
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
451
|
+
- Harness: `.local/lib-test-beta-0.2.0-beta1`
|
|
452
|
+
- Backend: MikroORM + SQLite locally, newly deployed Apps Script Gateway
|
|
453
|
+
- Database: fresh `/private/tmp/typed-sheets-progressive-20260726-01.sqlite`
|
|
454
|
+
- Gateway configuration: new untracked values supplied for this test; the
|
|
455
|
+
secret is intentionally not recorded here
|
|
456
|
+
- Provisioning: five projection sheets created and initialized successfully
|
|
457
|
+
- Background worker: enabled; reconciliation management loop enabled
|
|
458
|
+
- Traffic: write-only order workload (`LOAD_TEST_WRITE_RATIO=1`), 5 seconds per
|
|
459
|
+
stage, 20 customers and 20 products seeded at each stage, no drain wait
|
|
460
|
+
|
|
461
|
+
The server was started with the Gateway environment variables supplied for this
|
|
462
|
+
run and `TYPED_SHEETS_WORKER_MAX_EFFECTS=100`. Each stage reused the same local
|
|
463
|
+
database and newly provisioned spreadsheet so the table and outbox totals show
|
|
464
|
+
the cumulative effect of the stages.
|
|
465
|
+
|
|
466
|
+
| Stage | Concurrency | Orders | `/orders` req/s | p50 / p95 / p99 (ms) | Final outbox |
|
|
467
|
+
| --- | ---: | ---: | ---: | ---: | --- |
|
|
468
|
+
| 1 | 1 | 683 | 135.11 | 6.97 / 11.33 / 15.80 | applied 340, pending 4,084 |
|
|
469
|
+
| 2 | 2 | 582 | 112.71 | 15.53 / 27.20 / 36.95 | applied 340, failed 100, pending 8,431 |
|
|
470
|
+
| 3 | 4 | 481 | 93.66 | 42.46 / 67.13 / 141.94 | applied 340, failed 66, pending 12,462, processing 80 |
|
|
471
|
+
|
|
472
|
+
All API requests succeeded with zero HTTP errors. The SQLite totals after the
|
|
473
|
+
third stage were 1,746 orders, 3,385 order items, and 1,746 payments. The
|
|
474
|
+
write path therefore accepted traffic, but the projection path did not keep up:
|
|
475
|
+
the backlog increased by roughly 4,000 effects per stage while the applied
|
|
476
|
+
count remained at 340.
|
|
477
|
+
|
|
478
|
+
The server logs identified two concrete causes:
|
|
479
|
+
|
|
480
|
+
1. The reconciliation scan observed 340 Sheet rows but 4,434 desired SQLite
|
|
481
|
+
rows and enqueued 1,197 correction effects while the original outbox was
|
|
482
|
+
still being drained. Reconciliation is therefore amplifying the backlog
|
|
483
|
+
when it runs before the initial append queue has caught up.
|
|
484
|
+
2. The correction effects include the `_deleted` tombstone field, but the
|
|
485
|
+
registered ranges for the test sheets do not declare `_deleted`. Gateway
|
|
486
|
+
postcondition recovery repeatedly failed with:
|
|
487
|
+
|
|
488
|
+
```text
|
|
489
|
+
postcondition_read_failed: Effect field is not a registered header: _deleted
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
There was also a direct Gateway cost signal: a `readSnapshot` for a 320-row
|
|
493
|
+
sheet took 65.553 seconds. This is outside the fast append write itself, but it
|
|
494
|
+
shows that running full reconciliation snapshots during a high-write backlog
|
|
495
|
+
can monopolize the same worker and make convergence worse.
|
|
496
|
+
|
|
497
|
+
This run is not a successful convergence test. It demonstrates that the raw
|
|
498
|
+
SQLite API path remains responsive, while the current combination of pending
|
|
499
|
+
effect drain, immediate reconciliation, and an invalid tombstone field cannot
|
|
500
|
+
reach outbox zero.
|
|
501
|
+
|
|
502
|
+
## 2026-07-26 — pure fast-append Gateway throughput
|
|
503
|
+
|
|
504
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
505
|
+
- Harness: temporary `.local/fast-append-load-test.mjs`
|
|
506
|
+
- Backend: direct `AppsScriptOperationClient` + `createFastAppendRowsOperation`
|
|
507
|
+
calls; no SQLite
|
|
508
|
+
outbox, effect worker, reconciliation, snapshot, postcondition, receipt, or
|
|
509
|
+
delete operation was involved
|
|
510
|
+
- Database: `/private/tmp/typed-sheets-fast-append-20260726-01.sqlite` was used
|
|
511
|
+
only to provision the five projection registry entries; it was not part of
|
|
512
|
+
the measured write path
|
|
513
|
+
- Spreadsheet: newly provisioned `LoadTest_Customers` projection
|
|
514
|
+
- Gateway mode: the newly deployed fast-append-only Code.gs; a verification
|
|
515
|
+
`readSnapshot` request was rejected with `Fast append benchmark accepts only
|
|
516
|
+
fastAppendRows.`
|
|
517
|
+
- Request limit: 100 rows per Gateway call; the 200-row stage used two calls
|
|
518
|
+
|
|
519
|
+
| Batch rows | Gateway calls | Applied rows | Elapsed | Rows/s | Cells/s |
|
|
520
|
+
| ---: | ---: | ---: | ---: | ---: | ---: |
|
|
521
|
+
| 20 | 1 | 20 | 3,309.04 ms | 6.04 | 36.26 |
|
|
522
|
+
| 50 | 1 | 50 | 2,766.91 ms | 18.07 | 108.42 |
|
|
523
|
+
| 100 | 1 | 100 | 2,506.87 ms | 39.89 | 239.34 |
|
|
524
|
+
| 200 | 2 | 200 | 5,459.14 ms | 36.64 | 219.82 |
|
|
525
|
+
|
|
526
|
+
All 370 requested rows were acknowledged as `applied`; no row was left for a
|
|
527
|
+
worker or reconciliation pass. This is the first clean measurement of the
|
|
528
|
+
Gateway's current fast-append path. It confirms that the pure path can process
|
|
529
|
+
the complete tested load, but its steady-state ceiling is approximately 36–40
|
|
530
|
+
rows/s for six-column rows, with roughly 2.5–2.8 seconds of latency per 50–100
|
|
531
|
+
row request. The earlier backlog and `_deleted` failures were therefore not
|
|
532
|
+
part of this measurement.
|
|
533
|
+
|
|
534
|
+
## 2026-07-26 — continuous drain follow-up
|
|
535
|
+
|
|
536
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
537
|
+
- Harness: `.local/lib-test-beta-0.2.0-beta1`
|
|
538
|
+
- Backend: the same local SQLite database and new Sheet from the preceding
|
|
539
|
+
clean-Sheet test
|
|
540
|
+
- Server command:
|
|
541
|
+
|
|
542
|
+
```sh
|
|
543
|
+
TYPED_SHEETS_DB_PATH=/private/tmp/typed-sheets-new-sheet-20260726.sqlite \
|
|
544
|
+
TYPED_SHEETS_API_PORT=3200 TYPED_SHEETS_BACKGROUND_WORKER=1 npm start
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
- The server was restarted with the previous outbox intact. A local metrics
|
|
548
|
+
poll ran every 15 seconds for 180 seconds; no new application traffic was
|
|
549
|
+
generated.
|
|
550
|
+
|
|
551
|
+
| Metric | Result |
|
|
552
|
+
| --- | ---: |
|
|
553
|
+
| Initial recovered state | applied 180 / pending 2,084 / processing 80 / failed 20 |
|
|
554
|
+
| Applied during observation | +240 |
|
|
555
|
+
| Final applied effects | 420 |
|
|
556
|
+
| Final pending effects | 2,337 |
|
|
557
|
+
| Final processing effects | 0 |
|
|
558
|
+
| Final failed effects | 20 |
|
|
559
|
+
| Observation duration | 180 s |
|
|
560
|
+
|
|
561
|
+
The worker did continue consuming effects after the load runner had stopped;
|
|
562
|
+
this disproves the idea that the 120-second test timeout closes the Gateway or
|
|
563
|
+
stops synchronization. However, the queue did not converge. Reconciliation
|
|
564
|
+
continued to enqueue correction effects while the worker was consuming the
|
|
565
|
+
existing queue, so pending temporarily increased from 2,104 to 2,417 before
|
|
566
|
+
ending at 2,337.
|
|
567
|
+
|
|
568
|
+
The 20 failed effects were repeatedly rejected during batched postcondition
|
|
569
|
+
recovery with:
|
|
570
|
+
|
|
571
|
+
```text
|
|
572
|
+
postcondition_read_failed: Effect field is not a registered header: _deleted
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Those effects reached four attempts and remained failed. This is a concrete
|
|
576
|
+
blocking defect independent of the 120-second drain window: the worker can
|
|
577
|
+
consume some work indefinitely, but these effects cannot complete until the
|
|
578
|
+
registered Sheet headers and the postcondition payload agree. The continuous
|
|
579
|
+
test therefore confirms ongoing consumption, but not eventual convergence.
|
|
580
|
+
|
|
581
|
+
## 2026-07-27 — lib-test-beta thin-interface fast-append benchmark
|
|
582
|
+
|
|
583
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
584
|
+
- Historical harness: `.local/lib-test-beta-0.2.0-beta1/fast-append-load-test.mjs`
|
|
585
|
+
(removed after this benchmark)
|
|
586
|
+
- Server: `.local/lib-test-beta-0.2.0-beta1/server.mjs`, using the current
|
|
587
|
+
`AppsScriptOperationClient` and `createFastAppendRowsOperation` library APIs
|
|
588
|
+
- Database: fresh `/private/tmp/typed-sheets-lib-test-fast-append-20260727.sqlite`
|
|
589
|
+
used only to initialize the lib-test runtime; SQLite outbox/effect processing
|
|
590
|
+
was not part of the measured path
|
|
591
|
+
- Gateway: newly supplied deployment; URL, shared secret, and spreadsheet ID
|
|
592
|
+
are intentionally not recorded
|
|
593
|
+
- Background effect worker: disabled
|
|
594
|
+
- Reconciliation: disabled
|
|
595
|
+
- Setup: one unmeasured operation created/initialized `LoadTest_Customers` and
|
|
596
|
+
wrote its six-column header
|
|
597
|
+
- Measured scenario: three sequential requests, one contiguous `setValues()`
|
|
598
|
+
operation per stage; no metadata, snapshot, CAS, receipt, postcondition, or
|
|
599
|
+
delete work
|
|
600
|
+
|
|
601
|
+
Command used for the measured runner:
|
|
602
|
+
|
|
603
|
+
```sh
|
|
604
|
+
LOAD_TEST_BASE_URL=http://127.0.0.1:3205 \
|
|
605
|
+
node .local/lib-test-beta-0.2.0-beta1/fast-append-load-test.mjs
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
| Batch rows | Gateway calls | Applied rows | Elapsed | Rows/s | Cells/s |
|
|
609
|
+
| ---: | ---: | ---: | ---: | ---: | ---: |
|
|
610
|
+
| 20 | 1 | 20 | 2,275 ms | 8.79 | 52.75 |
|
|
611
|
+
| 100 | 1 | 100 | 2,729 ms | 36.64 | 219.86 |
|
|
612
|
+
| 370 | 1 | 370 | 3,792 ms | 97.57 | 585.44 |
|
|
613
|
+
|
|
614
|
+
All 490 rows across the three stages returned `applied`, with HTTP 200 and no
|
|
615
|
+
client or remote error. The 20-row stage includes the fixed HTTP/Apps Script
|
|
616
|
+
startup cost; larger contiguous writes amortized that cost substantially. The
|
|
617
|
+
370-row one-request result is materially faster than the earlier 200-row
|
|
618
|
+
two-request measurement (36.64 rows/s), but it is not a complete production
|
|
619
|
+
sync test because it bypasses SQLite outbox consumption and reconciliation.
|
|
620
|
+
|
|
621
|
+
## 2026-07-27 — operational User/Order/OrderItem end-to-end test
|
|
622
|
+
|
|
623
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
624
|
+
- Harness: `.local/typed-sheets-e2e.mjs`
|
|
625
|
+
- Command: `npm run build`, then `node .local/typed-sheets-e2e.mjs` with the
|
|
626
|
+
supplied Gateway environment variables; secret and Sheet ID are intentionally
|
|
627
|
+
not recorded
|
|
628
|
+
- Package boundary: the server imported the built package entry
|
|
629
|
+
(`dist/index.js`), `typed-sheets/orm`, and `typed-sheets/mikro-orm`; the test
|
|
630
|
+
runner rejected a `src` import and did not call the Gateway directly
|
|
631
|
+
- Domain tables: `User`, `Order`, and `OrderItem`; the library's internal
|
|
632
|
+
registry and outbox tables were also created by the MikroORM SQLite adapter
|
|
633
|
+
- Gateway: the supplied thin `Code.gs` deployment; provisioning and all remote
|
|
634
|
+
writes went through `AppsScriptOperationClient` and
|
|
635
|
+
`AppsScriptOperationSyncGateway`
|
|
636
|
+
- Spreadsheet setup: each run used three unique tabs derived from its run ID,
|
|
637
|
+
so existing Sheet data was neither deleted nor reused
|
|
638
|
+
- Worker: started automatically by the server after successful provisioning;
|
|
639
|
+
maximum 100 effects per pass
|
|
640
|
+
- Reconciliation: disabled for this isolated SQLite-to-Sheets create path
|
|
641
|
+
- API concurrency: 4 HTTP clients
|
|
642
|
+
|
|
643
|
+
The functional smoke flow created one User, one Order, and two OrderItems, then
|
|
644
|
+
read the User-Order-OrderItem graph through the HTTP API. Each load stage used
|
|
645
|
+
the same API and EntityManager path, generated durable SQLite outbox effects,
|
|
646
|
+
and waited for the server-owned worker to drain them. The stage measurements
|
|
647
|
+
exclude one-time package build, SQLite migration, Sheet provisioning, and the
|
|
648
|
+
functional smoke setup; they are the no-setup/steady-state measurements.
|
|
649
|
+
|
|
650
|
+
| Stage | New Orders | New OrderItems | Setup excluded | Stage time | SQLite rows = Sheet rows | Failed effects |
|
|
651
|
+
| ---: | ---: | ---: | :---: | ---: | --- | ---: |
|
|
652
|
+
| Smoke | 1 | 2 | — | functional check | 1 User / 1 Order / 2 Items | 0 |
|
|
653
|
+
| 20 | 20 | 40 | yes | 12,673 ms | 1 / 21 / 42 | 0 |
|
|
654
|
+
| 100 | 100 | 200 | yes | 16,918 ms | 1 / 121 / 242 | 0 |
|
|
655
|
+
| 370 | 370 | 740 | yes | 40,196 ms | 1 / 491 / 982 | 0 |
|
|
656
|
+
|
|
657
|
+
The final cumulative worker count was 1,474 applied effects, with zero failed
|
|
658
|
+
effects and zero ready outbox effects. Every dedicated Sheet tab matched the
|
|
659
|
+
SQLite domain count. The server was stopped and restarted against the same
|
|
660
|
+
SQLite file; the original Order and its two OrderItems were read successfully
|
|
661
|
+
after restart.
|
|
662
|
+
|
|
663
|
+
## 2026-07-27 — operational test with idle-gated reconciliation
|
|
664
|
+
|
|
665
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
666
|
+
- Harness: `.local/typed-sheets-e2e.mjs`
|
|
667
|
+
- Backend: built `typed-sheets` package, MikroORM, and local SQLite
|
|
668
|
+
- Gateway: the previously supplied Apps Script deployment; credentials and
|
|
669
|
+
spreadsheet ID are intentionally not recorded
|
|
670
|
+
- Scenario command (Gateway environment variables were supplied separately):
|
|
671
|
+
|
|
672
|
+
```sh
|
|
673
|
+
TYPED_SHEETS_OPERATIONAL_LOAD_STAGES=1 \
|
|
674
|
+
TYPED_SHEETS_OPERATIONAL_RECONCILIATION_INTERVAL_MS=1000 \
|
|
675
|
+
TYPED_SHEETS_OPERATIONAL_DRAIN_TIMEOUT_MS=120000 \
|
|
676
|
+
node .local/typed-sheets-e2e.mjs
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
- Reconciliation policy: run only after the worker pass is idle and SQLite has
|
|
680
|
+
no `pending` or `processing` outbox effects
|
|
681
|
+
- Dataset: one functional smoke flow, followed by one additional Order and two
|
|
682
|
+
OrderItems through the HTTP API
|
|
683
|
+
- Setup: package build, SQLite migration, Sheet provisioning, and functional
|
|
684
|
+
smoke setup are excluded from the stage timing
|
|
685
|
+
|
|
686
|
+
| Stage | New Orders | New OrderItems | Stage time | SQLite rows = Sheet rows | Failed effects |
|
|
687
|
+
| ---: | ---: | ---: | ---: | --- | ---: |
|
|
688
|
+
| 1 | 1 | 2 | 38,724 ms | 1 User / 2 Orders / 4 Items | 0 |
|
|
689
|
+
|
|
690
|
+
The operational E2E completed successfully. It imported the built package,
|
|
691
|
+
created all three domain tables through the library, wrote through the SQLite
|
|
692
|
+
outbox and server-owned worker, read the entity graph through the HTTP API, and
|
|
693
|
+
verified the same row counts in Sheets. The server restart check also passed.
|
|
694
|
+
The final outbox contained 21 applied effects and zero pending or processing
|
|
695
|
+
effects.
|
|
696
|
+
|
|
697
|
+
The test also exposed a remaining system-level issue. Even after the worker
|
|
698
|
+
became idle, reconciliation observed a stale or incomplete Sheet snapshot and
|
|
699
|
+
created correction effects:
|
|
700
|
+
|
|
701
|
+
| Reconciliation observation | Desired rows | Drifted rows | Missing rows | Correction effects |
|
|
702
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
703
|
+
| First scan | 4 | 0 | 4 | 4 |
|
|
704
|
+
| Later scan | 7 | 4 | 3 | 7 |
|
|
705
|
+
|
|
706
|
+
Fourteen reconciliation effects were eventually applied, which is why the
|
|
707
|
+
final row counts converged. This means the idle gate prevents reconciliation
|
|
708
|
+
from competing with an actively pending outbox, but it does not yet prove that
|
|
709
|
+
an `applied` worker result and the subsequent Sheet snapshot are immediately
|
|
710
|
+
consistent. The normal write result, Gateway snapshot behavior, and
|
|
711
|
+
reconciliation identity matching need separate investigation before claiming
|
|
712
|
+
that the correction path is only a rare safety net.
|
|
713
|
+
|
|
714
|
+
Compared with the earlier pure fast-append benchmark (370 synthetic rows in
|
|
715
|
+
3,792 ms), this run is not a raw `setValues()` comparison: it includes 1,110
|
|
716
|
+
entity effects, SQLite flush/outbox work, worker leasing, multiple Gateway
|
|
717
|
+
requests, and Sheet row-count verification. It confirms that the current
|
|
718
|
+
library-to-worker-to-thin-Gateway create path converges for this workload, not
|
|
719
|
+
that update/delete or reconciliation paths are complete.
|
|
720
|
+
|
|
721
|
+
## 2026-07-27 — batched anchor lookup polling benchmark
|
|
722
|
+
|
|
723
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
724
|
+
- Harness: `.local/typed-sheets-e2e.mjs`
|
|
725
|
+
- Backend: built `typed-sheets` package, MikroORM, and local SQLite
|
|
726
|
+
- Gateway: an operational `Code.gs` deployment; URL, secret, and spreadsheet ID
|
|
727
|
+
are intentionally not recorded
|
|
728
|
+
- Reconciliation: disabled so it could not add correction effects during the
|
|
729
|
+
polling measurement
|
|
730
|
+
- Scenario: create one smoke User plus 20 Orders and 40 OrderItems through the
|
|
731
|
+
HTTP API, wait for the server-owned effect worker to drain, then run the
|
|
732
|
+
polling path twice
|
|
733
|
+
- Command shape (Gateway environment variables were supplied separately):
|
|
734
|
+
|
|
735
|
+
```sh
|
|
736
|
+
npm run build
|
|
737
|
+
TYPED_SHEETS_OPERATIONAL_RECONCILIATION=0 \
|
|
738
|
+
TYPED_SHEETS_OPERATIONAL_POLLING_BENCHMARK=1 \
|
|
739
|
+
TYPED_SHEETS_OPERATIONAL_POLLING_RUNS=2 \
|
|
740
|
+
TYPED_SHEETS_OPERATIONAL_LOAD_STAGES=20 \
|
|
741
|
+
node .local/typed-sheets-e2e.mjs
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
The observation operation was changed from one `getDeveloperMetadata()` call
|
|
745
|
+
per row to one Sheet-scoped `DeveloperMetadataFinder` search per operation.
|
|
746
|
+
The returned row locations are indexed in memory and reused by both anchor
|
|
747
|
+
assignment and snapshot construction. Existing duplicate-anchor and
|
|
748
|
+
unanchored-row behavior remains unchanged.
|
|
749
|
+
|
|
750
|
+
| Poll run | Rows scanned | Anchors assigned | Users | Orders | OrderItems | Poll time |
|
|
751
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
752
|
+
| First (includes anchor setup) | 64 | 64 | 1 | 21 | 42 | 56,159 ms |
|
|
753
|
+
| Second (steady state) | 64 | 0 | 1 | 21 | 42 | 55,401 ms |
|
|
754
|
+
|
|
755
|
+
The load stage itself took 12,483 ms and produced matching SQLite/Sheet row
|
|
756
|
+
counts with zero failed effects. The first poll assigned one anchor per row;
|
|
757
|
+
this write remains intentionally per-row because `fastAppend` does not create
|
|
758
|
+
Developer Metadata. The second poll had no anchor writes, but its duration was
|
|
759
|
+
almost unchanged. Therefore the batch lookup removes the explicit row-by-row
|
|
760
|
+
metadata-read pattern, but it is not yet a material end-to-end polling speedup.
|
|
761
|
+
|
|
762
|
+
The remaining polling cost is now likely distributed across the full range
|
|
763
|
+
reads (`values`, formulas, display values, and merged ranges), per-cell
|
|
764
|
+
normalization and SHA-256 hashing, and conversion of the returned metadata
|
|
765
|
+
locations. A phase-level benchmark is required before claiming that any one of
|
|
766
|
+
those is the next dominant stage. This result also means the earlier estimate
|
|
767
|
+
of a few minutes for 10,000 rows cannot be accepted as a measured expectation;
|
|
768
|
+
the current steady-state result is still too slow to extrapolate safely.
|
|
769
|
+
|
|
770
|
+
## 2026-07-27 — operational timing run on the newly deployed Gateway
|
|
771
|
+
|
|
772
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
773
|
+
- Harness: `.local/typed-sheets-e2e.mjs`
|
|
774
|
+
- Backend: built `typed-sheets` package, MikroORM, and local SQLite
|
|
775
|
+
- Package boundary: the server imported `dist/index.js`; the runner did not
|
|
776
|
+
import `src/**` or call SQLite/Gateway operations directly
|
|
777
|
+
- Gateway: the newly supplied Apps Script deployment; URL, secret, and
|
|
778
|
+
spreadsheet ID are intentionally not recorded
|
|
779
|
+
- Reconciliation: disabled for this isolated write-path measurement
|
|
780
|
+
- API concurrency: 4; load stage: 20 new Orders with 40 new OrderItems
|
|
781
|
+
- Sheet setup: three unique projection tabs were provisioned for this run;
|
|
782
|
+
setup and provisioning are excluded from the stage timer
|
|
783
|
+
- Command shape (Gateway environment variables were supplied separately):
|
|
784
|
+
|
|
785
|
+
```sh
|
|
786
|
+
TYPED_SHEETS_OPERATIONAL_RECONCILIATION=0 \
|
|
787
|
+
TYPED_SHEETS_OPERATIONAL_LOAD_STAGES=20 \
|
|
788
|
+
TYPED_SHEETS_OPERATIONAL_DRAIN_TIMEOUT_MS=240000 \
|
|
789
|
+
node .local/typed-sheets-e2e.mjs
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
The functional smoke path created a User, an Order, and two OrderItems,
|
|
793
|
+
read the entity graph through the HTTP API, updated an Order through the
|
|
794
|
+
EntityManager, and removed the Order and its two related test items. Delete is
|
|
795
|
+
represented in the System_State projection by the library's
|
|
796
|
+
`__typed_sheets_deleted` tombstone, so the verification compares active rows,
|
|
797
|
+
not physical Sheet row count.
|
|
798
|
+
|
|
799
|
+
| Stage | New Orders | New OrderItems | Stage time | SQLite active rows = Sheet active rows | Failed effects |
|
|
800
|
+
| ---: | ---: | ---: | ---: | --- | ---: |
|
|
801
|
+
| Smoke | 1 | 2 | functional check | 1 User / 1 Order / 2 Items | 0 |
|
|
802
|
+
| 20 | 20 | 40 | 28,182 ms | 1 / 21 / 42 | 0 |
|
|
803
|
+
|
|
804
|
+
The stage finished with 69 applied effects, zero failed effects, and no ready
|
|
805
|
+
or active effects. The server was stopped and restarted against the same
|
|
806
|
+
SQLite file; the original Order and its two OrderItems were readable after
|
|
807
|
+
restart. All remote requests returned HTTP 200.
|
|
808
|
+
|
|
809
|
+
The timing sink separated local ORM work, worker orchestration, and the remote
|
|
810
|
+
Gateway. The largest steady-state totals were:
|
|
811
|
+
|
|
812
|
+
| Scope | Operation | Phase | Calls | Total | Max |
|
|
813
|
+
| --- | --- | --- | ---: | ---: | ---: |
|
|
814
|
+
| Worker | append | `append_gateway_dispatch` | 9 | 21,698 ms | 4,301 ms |
|
|
815
|
+
| Worker | update-like | `regular_gateway_dispatch` | 3 | 12,512 ms | 5,217 ms |
|
|
816
|
+
| Gateway | update-like | `dispatcher_eval` | 3 | 6,553 ms | 2,920 ms |
|
|
817
|
+
| Gateway | update-like | `script_lock` | 3 | 6,318 ms | 2,830 ms |
|
|
818
|
+
| Gateway | append | `dispatcher_flush` | 9 | 2,091 ms | 293 ms |
|
|
819
|
+
| Gateway | append | `append_range_lookup` | 9 | 896 ms | 248 ms |
|
|
820
|
+
| Gateway | append | `set_values` | 9 | 92 ms | 17 ms |
|
|
821
|
+
| ORM flush | append | `flush_total` | 23 | 55 ms | 4 ms |
|
|
822
|
+
| ORM flush | delete | `flush_total` | 1 | 2 ms | 2 ms |
|
|
823
|
+
|
|
824
|
+
This run confirms that local SQLite persistence and outbox creation are not the
|
|
825
|
+
current bottleneck: ORM flushes were measured in milliseconds. The dominant
|
|
826
|
+
cost is the Gateway round trip and Apps Script dispatcher/lock overhead. The
|
|
827
|
+
raw append `setValues()` work itself was only 92 ms across nine requests; the
|
|
828
|
+
remaining append time is request dispatch, range lookup, and remote execution
|
|
829
|
+
overhead. At the ORM layer `em.remove()` is correctly classified as `delete`,
|
|
830
|
+
but its System_State materialization is a tombstone write and therefore appears
|
|
831
|
+
in the Gateway's regular update-like timing path rather than as a physical row
|
|
832
|
+
delete.
|
|
833
|
+
|
|
834
|
+
This is a successful functional and timing run for the 20-order operational
|
|
835
|
+
scenario. It does not establish the sustained 100/370-order drain ceiling, nor
|
|
836
|
+
does it test reconciliation or user-edit conflict handling; those require
|
|
837
|
+
separate runs so their reads and corrective effects do not contaminate this
|
|
838
|
+
write-path measurement.
|
|
839
|
+
|
|
840
|
+
## 2026-07-27 — progressive operational throughput run
|
|
841
|
+
|
|
842
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
843
|
+
- Harness: `.local/typed-sheets-e2e.mjs`
|
|
844
|
+
- Backend: built `typed-sheets` package, MikroORM, and local SQLite
|
|
845
|
+
- Package boundary: the server imported `dist/index.js`; the runner did not
|
|
846
|
+
import `src/**` or call SQLite/Gateway operations directly
|
|
847
|
+
- Gateway: the newly supplied Apps Script deployment; credentials are not
|
|
848
|
+
recorded
|
|
849
|
+
- Reconciliation: disabled for this isolated throughput measurement
|
|
850
|
+
- Stage Sheet verification: disabled so the result measures API ingestion plus
|
|
851
|
+
worker drain, without an additional full snapshot read after every stage
|
|
852
|
+
- Load stages: 20, 100, and 370 new Orders; each Order created two OrderItems
|
|
853
|
+
- API concurrency: 4; Gateway timeout: 120 seconds; drain timeout: 240 seconds
|
|
854
|
+
- Command shape (Gateway environment variables were supplied separately):
|
|
855
|
+
|
|
856
|
+
```sh
|
|
857
|
+
TYPED_SHEETS_OPERATIONAL_RECONCILIATION=0 \
|
|
858
|
+
TYPED_SHEETS_OPERATIONAL_LOAD_STAGES='20,100,370' \
|
|
859
|
+
TYPED_SHEETS_OPERATIONAL_VERIFY_STAGE_SHEETS=0 \
|
|
860
|
+
TYPED_SHEETS_OPERATIONAL_DRAIN_TIMEOUT_MS=240000 \
|
|
861
|
+
node .local/typed-sheets-e2e.mjs
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
| Stage | New Orders | New OrderItems | Materialized rows | Stage time | Approx. rows/s | Cumulative applied | Failed |
|
|
865
|
+
| ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
866
|
+
| 20 | 20 | 40 | 60 | 11,987 ms | 5.0 | 69 | 0 |
|
|
867
|
+
| 100 | 100 | 200 | 300 | 14,960 ms | 20.1 | 369 | 0 |
|
|
868
|
+
| 370 | 370 | 740 | 1,110 | 36,865 ms | 30.1 | 1,479 | 0 |
|
|
869
|
+
|
|
870
|
+
All three stages completed through the real HTTP server and the deployed
|
|
871
|
+
Gateway. The worker reached the expected applied count at every stage, no
|
|
872
|
+
effect failed, the server restarted successfully against the same SQLite file,
|
|
873
|
+
and the persisted Order/OrderItem graph remained readable after restart.
|
|
874
|
+
|
|
875
|
+
The dominant measured cost was still the remote worker-to-Gateway path:
|
|
876
|
+
|
|
877
|
+
| Scope | Phase | Calls | Total | Max |
|
|
878
|
+
| --- | --- | ---: | ---: | ---: |
|
|
879
|
+
| Worker | `append_gateway_dispatch` | 27 | 67,825 ms | 3,964 ms |
|
|
880
|
+
| Worker | `worker_total` (append) | 20 | 71,592 ms | 6,068 ms |
|
|
881
|
+
| Gateway | `append_range_lookup` | 27 | 3,941 ms | 938 ms |
|
|
882
|
+
| Gateway | `set_values` | 27 | 370 ms | 52 ms |
|
|
883
|
+
| ORM flush | `flush_total` (append) | 493 | 1,118 ms | 7 ms |
|
|
884
|
+
|
|
885
|
+
This separates the bottleneck more clearly: local SQLite/ORM flush work is
|
|
886
|
+
roughly millisecond-scale, and the raw Gateway `setValues()` work is small.
|
|
887
|
+
Most elapsed time is HTTP/Apps Script dispatch and Gateway range lookup. The
|
|
888
|
+
stage timer includes API entity creation and worker drain, so it is an
|
|
889
|
+
end-to-end operational throughput result, not a raw `setValues()` benchmark.
|
|
890
|
+
|
|
891
|
+
The polling probe returned zero rows and zero tables on both steady-state runs.
|
|
892
|
+
That is **not** a measured user-input polling speed: this operational fixture
|
|
893
|
+
registers only `system_state` projections, so there is no `user_input`
|
|
894
|
+
projection for the optimized polling path to scan. User-input polling needs a
|
|
895
|
+
separate fixture with a valid user-input registration and data.
|
|
896
|
+
|
|
897
|
+
## 2026-07-27 — corrected full-table polling run
|
|
898
|
+
|
|
899
|
+
The previous section's polling probe was invalid for this operational scenario:
|
|
900
|
+
it filtered by the projection label `user_input`, even though the test's User,
|
|
901
|
+
Order, and OrderItem tabs are all domain tables represented by `system_state`.
|
|
902
|
+
The polling helper now batches all registered projections; `user_input` remains
|
|
903
|
+
an ownership/edit-mode classification, not a domain table name.
|
|
904
|
+
|
|
905
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
906
|
+
- Harness: `.local/typed-sheets-e2e.mjs`
|
|
907
|
+
- Backend and package boundary: same as the progressive throughput run above
|
|
908
|
+
- Reconciliation: disabled
|
|
909
|
+
- Load stage: 20 Orders and 40 OrderItems, plus the functional smoke rows
|
|
910
|
+
- Polling: one combined Gateway observation request for User, Order, and
|
|
911
|
+
OrderItem, followed by two polling passes
|
|
912
|
+
- Command shape (Gateway environment variables were supplied separately):
|
|
913
|
+
|
|
914
|
+
```sh
|
|
915
|
+
TYPED_SHEETS_OPERATIONAL_RECONCILIATION=0 \
|
|
916
|
+
TYPED_SHEETS_OPERATIONAL_LOAD_STAGES=20 \
|
|
917
|
+
TYPED_SHEETS_OPERATIONAL_POLLING_BENCHMARK=1 \
|
|
918
|
+
TYPED_SHEETS_OPERATIONAL_POLLING_RUNS=2 \
|
|
919
|
+
TYPED_SHEETS_OPERATIONAL_VERIFY_STAGE_SHEETS=0 \
|
|
920
|
+
node .local/typed-sheets-e2e.mjs
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
| Poll pass | Rows scanned | Anchors assigned | User rows | Order rows | OrderItem rows | Elapsed |
|
|
924
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
925
|
+
| First (anchor setup) | 66 | 60 | 1 | 22 | 43 | 34,919 ms |
|
|
926
|
+
| Second (steady state) | 66 | 0 | 1 | 22 | 43 | 75,150 ms |
|
|
927
|
+
|
|
928
|
+
Both passes completed without failed effects. The first pass assigned anchors
|
|
929
|
+
to rows created by fast-append; the second pass reused all 66 anchors. The
|
|
930
|
+
steady-state pass was slower than the first despite doing no metadata writes,
|
|
931
|
+
so the dominant polling cost is not only anchor assignment. The full snapshot
|
|
932
|
+
read, Apps Script lock/dispatch latency, range reads, normalization, hashing,
|
|
933
|
+
and SQLite observation persistence all remain in the path.
|
|
934
|
+
|
|
935
|
+
A separate 20/100/370 run reached the 370 stage but could not start its full
|
|
936
|
+
table polling pass: the Gateway rejected the Order observation while adding
|
|
937
|
+
row Developer Metadata because the test Spreadsheet exceeded its allowed
|
|
938
|
+
metadata storage. This is an independent scalability limit in the current
|
|
939
|
+
anchor-based polling design; it prevents claiming a 370-row polling result.
|
|
940
|
+
|
|
941
|
+
## 2026-07-27 — polling phase trace
|
|
942
|
+
|
|
943
|
+
The observation Gateway and local ingestion path were instrumented to separate
|
|
944
|
+
remote Apps Script work from SQLite comparison and observation persistence. The
|
|
945
|
+
same 20-Order/40-OrderItem operational fixture scanned 66 rows across the three
|
|
946
|
+
domain tabs.
|
|
947
|
+
|
|
948
|
+
| Poll pass | User Gateway | Order Gateway | OrderItem Gateway | Local SQLite/observation | Total polling |
|
|
949
|
+
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
950
|
+
| First, 60 anchors assigned | 994 ms | 8,917 ms | 18,464 ms | 8 ms | 31,371 ms |
|
|
951
|
+
| Second, steady state | 635 ms | 6,748 ms | 17,661 ms | 6 ms | 27,652 ms |
|
|
952
|
+
|
|
953
|
+
The first pass spent 14,544 ms assigning 60 row anchors: 4,808 ms for Order
|
|
954
|
+
and 9,735 ms for OrderItem. The second pass assigned no anchors, but still
|
|
955
|
+
spent 10,402 ms reading Developer Metadata, so removing anchor writes alone is
|
|
956
|
+
not enough.
|
|
957
|
+
|
|
958
|
+
The largest steady-state remote phases were:
|
|
959
|
+
|
|
960
|
+
| Phase | User | Order | OrderItem | Combined |
|
|
961
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
962
|
+
| `anchor_metadata_read` | 228 ms | 2,794 ms | 7,380 ms | 10,402 ms |
|
|
963
|
+
| `row_normalization` | 30 ms | 786 ms | 5,705 ms | 6,521 ms |
|
|
964
|
+
| `snapshot_hash` | 111 ms | 2,502 ms | 4,204 ms | 6,817 ms |
|
|
965
|
+
|
|
966
|
+
These phases are nested inside `snapshot_build`, so their combined values must
|
|
967
|
+
not be added again to the per-table Gateway totals. The raw `values_read`
|
|
968
|
+
phase was only 12 ms in steady state, and local SQLite shadow comparison plus
|
|
969
|
+
observation persistence took 6 ms. Therefore the current polling bottleneck is
|
|
970
|
+
not SQLite and not the basic Sheet range read; it is the Developer Metadata
|
|
971
|
+
finder plus repeated Apps Script hashing/normalization work. OrderItem alone
|
|
972
|
+
accounted for 17,661 ms of the 27,652 ms steady-state pass.
|
|
973
|
+
|
|
974
|
+
This confirms that the current full-snapshot polling path is not suitable as a
|
|
975
|
+
frequent normal synchronization mechanism at this size. It is currently more
|
|
976
|
+
appropriate as an infrequent safety scan, while `onEdit` or a redesigned
|
|
977
|
+
lightweight identity/change path handles the normal user-edit signal. The
|
|
978
|
+
370-row polling pass remains unmeasured because the current anchor metadata
|
|
979
|
+
quota is reached before the scan completes.
|
|
980
|
+
|
|
981
|
+
## 2026-07-27 — lightweight values-only polling
|
|
982
|
+
|
|
983
|
+
- Branch: `refactor/thin-sync-gateway`
|
|
984
|
+
- Harness: `.local/typed-sheets-e2e.mjs`
|
|
985
|
+
- Backend: built `typed-sheets` package, MikroORM, and local SQLite
|
|
986
|
+
- Package boundary: the server imported `dist/index.js`; the runner did not
|
|
987
|
+
import `src/**` or call SQLite/Gateway operations directly
|
|
988
|
+
- Gateway: the supplied Apps Script deployment; URL, secret, and spreadsheet
|
|
989
|
+
ID are intentionally not recorded
|
|
990
|
+
- Reconciliation: disabled
|
|
991
|
+
- Dataset: one smoke User plus 20 Orders and 40 OrderItems (66 Sheet rows)
|
|
992
|
+
- Scenario: the server-owned worker drained the outbox, then `/poll` issued one
|
|
993
|
+
batched values-only read for User, Order, and OrderItem twice
|
|
994
|
+
- Command shape (Gateway environment variables were supplied separately):
|
|
995
|
+
|
|
996
|
+
```sh
|
|
997
|
+
npm run build
|
|
998
|
+
TYPED_SHEETS_OPERATIONAL_RECONCILIATION=0 \
|
|
999
|
+
TYPED_SHEETS_OPERATIONAL_LOAD_STAGES=20 \
|
|
1000
|
+
TYPED_SHEETS_OPERATIONAL_POLLING_BENCHMARK=1 \
|
|
1001
|
+
TYPED_SHEETS_OPERATIONAL_POLLING_RUNS=2 \
|
|
1002
|
+
TYPED_SHEETS_OPERATIONAL_VERIFY_STAGE_SHEETS=0 \
|
|
1003
|
+
TYPED_SHEETS_OPERATIONAL_SKIP_TIMING_SUMMARY=1 \
|
|
1004
|
+
node .local/typed-sheets-e2e.mjs
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
| Poll pass | Rows scanned | Unchanged | Changed | Unknown/invalid | Elapsed | Remote read total |
|
|
1008
|
+
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
|
1009
|
+
| First | 66 | 66 | 0 | 0 / 0 | 2,109 ms | 573 ms |
|
|
1010
|
+
| Second, steady state | 66 | 66 | 0 | 0 / 0 | 2,240 ms | 530 ms |
|
|
1011
|
+
|
|
1012
|
+
Each pass used one signed Apps Script request containing three independent
|
|
1013
|
+
`getValues()` operations. No `LockService`, Developer Metadata, snapshot hash,
|
|
1014
|
+
receipt, or observation persistence was used. The remote phases per table were
|
|
1015
|
+
approximately 210–220 ms for User, 155–210 ms for Order, and 156–163 ms for
|
|
1016
|
+
OrderItem; the remainder was HTTP dispatch and local canonical comparison.
|
|
1017
|
+
|
|
1018
|
+
The test passed through the real HTTP server, package boundary, SQLite outbox,
|
|
1019
|
+
background worker, and deployed Gateway. The final rows were classified as
|
|
1020
|
+
unchanged, including active and tombstoned System_State rows, and the server
|
|
1021
|
+
restart check passed. Compared with the previous steady-state full-snapshot
|
|
1022
|
+
poll (27,652 ms for the same 66-row shape), the lightweight path was roughly
|
|
1023
|
+
12 times faster. This is a read/compare benchmark: changed rows are returned
|
|
1024
|
+
to the caller, but this pass does not yet turn them into evaluated user-edit
|
|
1025
|
+
events or canonical writes.
|
|
1026
|
+
|
|
1027
|
+
## Caveats
|
|
1028
|
+
|
|
1029
|
+
- The raw benchmark measures a throughput upper bound, not production sync
|
|
1030
|
+
behavior.
|
|
1031
|
+
- The benchmark uses an isolated sheet and synthetic string values.
|
|
1032
|
+
- Network, HTTP, lock acquisition, retry, and response-loss behavior are not
|
|
1033
|
+
included.
|
|
1034
|
+
- The result strongly indicates that the current Gateway validation and
|
|
1035
|
+
metadata path, rather than raw `setValues()` throughput, is the dominant
|
|
1036
|
+
bottleneck.
|