@rocicorp/zero 1.2.0-canary.10 → 1.2.0-canary.12

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.
Files changed (204) hide show
  1. package/out/analyze-query/src/bin-analyze.js +25 -25
  2. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  3. package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
  4. package/out/ast-to-zql/src/ast-to-zql.js +2 -1
  5. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  6. package/out/replicache/src/btree/node.d.ts.map +1 -1
  7. package/out/replicache/src/btree/node.js +2 -2
  8. package/out/replicache/src/btree/node.js.map +1 -1
  9. package/out/replicache/src/connection-loop.js +3 -3
  10. package/out/replicache/src/connection-loop.js.map +1 -1
  11. package/out/replicache/src/deleted-clients.d.ts +0 -4
  12. package/out/replicache/src/deleted-clients.d.ts.map +1 -1
  13. package/out/replicache/src/deleted-clients.js +1 -1
  14. package/out/replicache/src/deleted-clients.js.map +1 -1
  15. package/out/replicache/src/hash.d.ts.map +1 -1
  16. package/out/replicache/src/hash.js.map +1 -1
  17. package/out/replicache/src/process-scheduler.d.ts.map +1 -1
  18. package/out/replicache/src/process-scheduler.js.map +1 -1
  19. package/out/replicache/src/request-idle.js +1 -1
  20. package/out/replicache/src/request-idle.js.map +1 -1
  21. package/out/replicache/src/sync/patch.d.ts +1 -1
  22. package/out/replicache/src/sync/patch.d.ts.map +1 -1
  23. package/out/replicache/src/sync/patch.js +1 -1
  24. package/out/replicache/src/sync/patch.js.map +1 -1
  25. package/out/shared/src/arrays.d.ts.map +1 -1
  26. package/out/shared/src/arrays.js +1 -2
  27. package/out/shared/src/arrays.js.map +1 -1
  28. package/out/shared/src/bigint-json.js +1 -1
  29. package/out/shared/src/bigint-json.js.map +1 -1
  30. package/out/shared/src/btree-set.js +1 -1
  31. package/out/shared/src/btree-set.js.map +1 -1
  32. package/out/shared/src/iterables.d.ts +7 -0
  33. package/out/shared/src/iterables.d.ts.map +1 -1
  34. package/out/shared/src/iterables.js +10 -1
  35. package/out/shared/src/iterables.js.map +1 -1
  36. package/out/shared/src/logging.d.ts.map +1 -1
  37. package/out/shared/src/logging.js +10 -9
  38. package/out/shared/src/logging.js.map +1 -1
  39. package/out/shared/src/options.js +1 -1
  40. package/out/shared/src/options.js.map +1 -1
  41. package/out/shared/src/tdigest-schema.d.ts.map +1 -1
  42. package/out/shared/src/tdigest-schema.js.map +1 -1
  43. package/out/shared/src/tdigest.d.ts.map +1 -1
  44. package/out/shared/src/tdigest.js +7 -7
  45. package/out/shared/src/tdigest.js.map +1 -1
  46. package/out/shared/src/valita.d.ts.map +1 -1
  47. package/out/shared/src/valita.js +1 -1
  48. package/out/shared/src/valita.js.map +1 -1
  49. package/out/z2s/src/sql.d.ts.map +1 -1
  50. package/out/z2s/src/sql.js +2 -1
  51. package/out/z2s/src/sql.js.map +1 -1
  52. package/out/zero/package.js +5 -6
  53. package/out/zero/package.js.map +1 -1
  54. package/out/zero/src/pg.js +1 -1
  55. package/out/zero/src/server.js +1 -1
  56. package/out/zero-cache/src/auth/load-permissions.js +1 -1
  57. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  58. package/out/zero-cache/src/config/zero-config.d.ts +8 -0
  59. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  60. package/out/zero-cache/src/config/zero-config.js +16 -0
  61. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  62. package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
  63. package/out/zero-cache/src/custom/fetch.js +2 -0
  64. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  65. package/out/zero-cache/src/custom-queries/transform-query.js +1 -1
  66. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  67. package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
  68. package/out/zero-cache/src/db/migration-lite.js +1 -1
  69. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  70. package/out/zero-cache/src/db/migration.d.ts.map +1 -1
  71. package/out/zero-cache/src/db/migration.js +1 -1
  72. package/out/zero-cache/src/db/migration.js.map +1 -1
  73. package/out/zero-cache/src/db/pg-copy-binary.d.ts +101 -0
  74. package/out/zero-cache/src/db/pg-copy-binary.d.ts.map +1 -0
  75. package/out/zero-cache/src/db/pg-copy-binary.js +381 -0
  76. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -0
  77. package/out/zero-cache/src/db/warmup.d.ts.map +1 -1
  78. package/out/zero-cache/src/db/warmup.js +3 -1
  79. package/out/zero-cache/src/db/warmup.js.map +1 -1
  80. package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +1 -1
  81. package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
  82. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  83. package/out/zero-cache/src/server/main.js +1 -1
  84. package/out/zero-cache/src/server/main.js.map +1 -1
  85. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  86. package/out/zero-cache/src/server/syncer.js +8 -10
  87. package/out/zero-cache/src/server/syncer.js.map +1 -1
  88. package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
  89. package/out/zero-cache/src/server/worker-urls.js +2 -1
  90. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  91. package/out/zero-cache/src/services/change-source/change-source.d.ts +4 -0
  92. package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
  93. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
  94. package/out/zero-cache/src/services/change-source/custom/change-source.js +7 -4
  95. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  96. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  97. package/out/zero-cache/src/services/change-source/pg/change-source.js +9 -1
  98. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  99. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +1 -0
  100. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  101. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +85 -5
  102. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  103. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -1
  104. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  105. package/out/zero-cache/src/services/change-streamer/broadcast.js +1 -1
  106. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  107. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +3 -3
  108. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  109. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +1 -0
  110. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  111. package/out/zero-cache/src/services/replicator/change-processor.js +1 -1
  112. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  113. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  114. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -1
  115. package/out/zero-cache/src/services/view-syncer/client-schema.js +4 -3
  116. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  117. package/out/zero-cache/src/services/view-syncer/cvr-store.js +2 -2
  118. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  119. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  120. package/out/zero-cache/src/services/view-syncer/cvr.js +4 -3
  121. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  122. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +3 -1
  123. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  124. package/out/zero-cache/src/services/view-syncer/snapshotter.js +1 -1
  125. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  126. package/out/zero-cache/src/services/view-syncer/view-syncer.js +2 -2
  127. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  128. package/out/zero-cache/src/types/lite.d.ts.map +1 -1
  129. package/out/zero-cache/src/types/lite.js +3 -2
  130. package/out/zero-cache/src/types/lite.js.map +1 -1
  131. package/out/zero-cache/src/types/pg-types.js +4 -1
  132. package/out/zero-cache/src/types/pg-types.js.map +1 -1
  133. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  134. package/out/zero-cache/src/types/pg.js +6 -1
  135. package/out/zero-cache/src/types/pg.js.map +1 -1
  136. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  137. package/out/zero-cache/src/types/subscription.js +2 -2
  138. package/out/zero-cache/src/types/subscription.js.map +1 -1
  139. package/out/zero-cache/src/workers/connection.js +2 -2
  140. package/out/zero-cache/src/workers/connection.js.map +1 -1
  141. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts +1 -1
  142. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  143. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +18 -2
  144. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  145. package/out/zero-cache/src/workers/syncer.d.ts +1 -1
  146. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  147. package/out/zero-cache/src/workers/syncer.js +5 -5
  148. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  149. package/out/zero-client/src/client/http-string.d.ts.map +1 -1
  150. package/out/zero-client/src/client/http-string.js.map +1 -1
  151. package/out/zero-client/src/client/metrics.d.ts.map +1 -1
  152. package/out/zero-client/src/client/metrics.js +2 -1
  153. package/out/zero-client/src/client/metrics.js.map +1 -1
  154. package/out/zero-client/src/client/server-option.js +1 -1
  155. package/out/zero-client/src/client/server-option.js.map +1 -1
  156. package/out/zero-client/src/client/version.js +1 -1
  157. package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
  158. package/out/zero-client/src/client/zero-poke-handler.js +1 -1
  159. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  160. package/out/zero-pg/src/mod.js +1 -1
  161. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  162. package/out/zero-protocol/src/ast.js.map +1 -1
  163. package/out/zero-protocol/src/primary-key.d.ts.map +1 -1
  164. package/out/zero-protocol/src/primary-key.js.map +1 -1
  165. package/out/zero-protocol/src/push.d.ts.map +1 -1
  166. package/out/zero-protocol/src/push.js.map +1 -1
  167. package/out/zero-schema/src/name-mapper.js +1 -1
  168. package/out/zero-schema/src/name-mapper.js.map +1 -1
  169. package/out/zero-server/src/mod.js +1 -1
  170. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  171. package/out/zero-server/src/process-mutations.js +2 -1
  172. package/out/zero-server/src/process-mutations.js.map +1 -1
  173. package/out/zero-server/src/push-processor.d.ts +1 -0
  174. package/out/zero-server/src/push-processor.d.ts.map +1 -1
  175. package/out/zero-server/src/push-processor.js +3 -2
  176. package/out/zero-server/src/push-processor.js.map +1 -1
  177. package/out/zql/src/builder/like.js +2 -1
  178. package/out/zql/src/builder/like.js.map +1 -1
  179. package/out/zql/src/ivm/data.d.ts.map +1 -1
  180. package/out/zql/src/ivm/data.js +6 -15
  181. package/out/zql/src/ivm/data.js.map +1 -1
  182. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  183. package/out/zql/src/ivm/memory-source.js +2 -4
  184. package/out/zql/src/ivm/memory-source.js.map +1 -1
  185. package/out/zql/src/query/complete-ordering.js +1 -1
  186. package/out/zql/src/query/complete-ordering.js.map +1 -1
  187. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  188. package/out/zql/src/query/query-impl.js +2 -2
  189. package/out/zql/src/query/query-impl.js.map +1 -1
  190. package/out/zql/src/query/query-registry.d.ts.map +1 -1
  191. package/out/zql/src/query/query-registry.js +2 -1
  192. package/out/zql/src/query/query-registry.js.map +1 -1
  193. package/out/zql/src/query/ttl.js +1 -1
  194. package/out/zql/src/query/ttl.js.map +1 -1
  195. package/out/zqlite/src/sqlite-cost-model.d.ts +1 -1
  196. package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
  197. package/out/zqlite/src/sqlite-cost-model.js +1 -1
  198. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  199. package/out/zqlite/src/sqlite-stat-fanout.js +1 -1
  200. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  201. package/out/zqlite/src/table-source.d.ts.map +1 -1
  202. package/out/zqlite/src/table-source.js +1 -1
  203. package/out/zqlite/src/table-source.js.map +1 -1
  204. package/package.json +5 -6
@@ -1 +1 @@
1
- {"version":3,"file":"anonymous-otel-start.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/anonymous-otel-start.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAOjD,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,MAAM,WAAW,GAAG;IACxB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAgfF,eAAO,MAAM,uBAAuB,GAAI,KAAK,UAAU,EAAE,SAAS,UAAU,SAC/C,CAAC;AAC9B,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,GAAG,QAAQ,EAAE,cAAS,SAC1B,CAAC;AACxC,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,GAAG,QAAQ,EAAE,cAAS,SAC1B,CAAC;AACrC,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,SACX,CAAC;AACpC,eAAO,MAAM,uBAAuB,YACC,CAAC;AACtC,eAAO,MAAM,yBAAyB,YACC,CAAC;AACxC,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,MAAM,SACjB,CAAC;AAChD,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,WAAW,SACtB,CAAC;AACzC,eAAO,MAAM,0BAA0B,YAA6B,CAAC"}
1
+ {"version":3,"file":"anonymous-otel-start.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/anonymous-otel-start.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAOjD,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,MAAM,WAAW,GAAG;IACxB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAkfF,eAAO,MAAM,uBAAuB,GAAI,KAAK,UAAU,EAAE,SAAS,UAAU,SAC/C,CAAC;AAC9B,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,GAAG,QAAQ,EAAE,cAAS,SAC1B,CAAC;AACxC,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,GAAG,QAAQ,EAAE,cAAS,SAC1B,CAAC;AACrC,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,SACX,CAAC;AACpC,eAAO,MAAM,uBAAuB,YACC,CAAC;AACtC,eAAO,MAAM,yBAAyB,YACC,CAAC;AACxC,eAAO,MAAM,2BAA2B,GAAI,QAAQ,MAAM,MAAM,SACjB,CAAC;AAChD,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,WAAW,SACtB,CAAC;AACzC,eAAO,MAAM,0BAA0B,YAA6B,CAAC"}
@@ -11,6 +11,7 @@ import { execSync } from "child_process";
11
11
  import { randomUUID } from "crypto";
12
12
  import { dirname, join } from "path";
13
13
  //#region ../zero-cache/src/server/anonymous-otel-start.ts
14
+ var hostNameRe = /^[a-f0-9]{12}$/;
14
15
  var AnonymousTelemetryManager = class AnonymousTelemetryManager {
15
16
  static #instance;
16
17
  #starting = false;
@@ -277,7 +278,7 @@ var AnonymousTelemetryManager = class AnonymousTelemetryManager {
277
278
  if (existsSync("/.dockerenv")) return true;
278
279
  if (existsSync("/usr/local/bin/docker-entrypoint.sh")) return true;
279
280
  if (process.env.KUBERNETES_SERVICE_HOST) return true;
280
- if (process.env.DOCKER_CONTAINER_ID || process.env.HOSTNAME?.match(/^[a-f0-9]{12}$/)) return true;
281
+ if (process.env.DOCKER_CONTAINER_ID || process.env.HOSTNAME?.match(hostNameRe)) return true;
281
282
  if (existsSync("/proc/1/cgroup")) {
282
283
  const cgroup = readFileSync("/proc/1/cgroup", "utf8");
283
284
  if (cgroup.includes("docker") || cgroup.includes("kubepods") || cgroup.includes("containerd")) return true;
@@ -1 +1 @@
1
- {"version":3,"file":"anonymous-otel-start.js","names":["#instance","#processId","#lc","#starting","#config","#viewSyncerCount","#cachedAttributes","#run","#stopped","#getAttributes","#meterProvider","#meter","#setupMetrics","#activeUsersGetter","#totalCrudMutations","#totalCustomMutations","#totalCrudQueries","#totalCustomQueries","#totalRowsSynced","#totalConnectionsSuccess","#totalConnectionsAttempted","#activeClientGroupsGetter","#getPlatform","#getGitProjectId","#getOrSetFsID","#findUp","#isInContainer"],"sources":["../../../../../zero-cache/src/server/anonymous-otel-start.ts"],"sourcesContent":["import type {ObservableResult} from '@opentelemetry/api';\nimport {type Meter} from '@opentelemetry/api';\nimport {OTLPMetricExporter} from '@opentelemetry/exporter-metrics-otlp-http';\nimport {resourceFromAttributes} from '@opentelemetry/resources';\nimport {\n MeterProvider,\n PeriodicExportingMetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport type {LogContext} from '@rocicorp/logger';\nimport {execSync} from 'child_process';\nimport {randomUUID} from 'crypto';\nimport {existsSync, mkdirSync, readFileSync, writeFileSync} from 'fs';\nimport {homedir, platform} from 'os';\nimport {dirname, join} from 'path';\nimport {h64} from '../../../shared/src/hash.js';\nimport {\n getServerVersion,\n getZeroConfig,\n type ZeroConfig,\n} from '../config/zero-config.js';\nimport {setupOtelDiagnosticLogger} from './otel-diag-logger.ts';\n\nexport type ActiveUsers = {\n active_users_last_day: number;\n users_1da: number;\n users_7da: number;\n users_30da: number;\n users_1da_legacy: number;\n users_7da_legacy: number;\n users_30da_legacy: number;\n};\n\nclass AnonymousTelemetryManager {\n static #instance: AnonymousTelemetryManager;\n #starting = false;\n #stopped = false;\n #meter!: Meter;\n #meterProvider!: MeterProvider;\n #totalCrudMutations = 0;\n #totalCustomMutations = 0;\n #totalCrudQueries = 0;\n #totalCustomQueries = 0;\n #totalRowsSynced = 0;\n #totalConnectionsSuccess = 0;\n #totalConnectionsAttempted = 0;\n #activeClientGroupsGetter: (() => number) | undefined;\n #activeUsersGetter: (() => ActiveUsers) | undefined;\n #lc: LogContext | undefined;\n #config: ZeroConfig | undefined;\n #processId: string;\n #cachedAttributes: Record<string, string> | undefined;\n #viewSyncerCount = 1;\n\n private constructor() {\n this.#processId = randomUUID();\n }\n\n static getInstance(): AnonymousTelemetryManager {\n if (!AnonymousTelemetryManager.#instance) {\n AnonymousTelemetryManager.#instance = new AnonymousTelemetryManager();\n }\n return AnonymousTelemetryManager.#instance;\n }\n\n start(lc?: LogContext, config?: ZeroConfig) {\n this.#lc = lc;\n\n // Set up OpenTelemetry diagnostic logger if not already configured\n setupOtelDiagnosticLogger(lc);\n\n if (!config) {\n try {\n config = getZeroConfig();\n } catch (e) {\n this.#lc?.info?.('telemetry: disabled - unable to parse config', e);\n return;\n }\n }\n\n if (process.env.DO_NOT_TRACK) {\n this.#lc?.info?.(\n 'telemetry: disabled - DO_NOT_TRACK environment variable is set',\n );\n return;\n }\n\n if (!config.enableTelemetry) {\n this.#lc?.info?.('telemetry: disabled - enableTelemetry is false');\n return;\n }\n\n if (this.#starting) {\n return;\n }\n\n this.#starting = true;\n this.#config = config;\n this.#viewSyncerCount = config.numSyncWorkers ?? 1;\n this.#cachedAttributes = undefined;\n\n this.#lc?.info?.(`telemetry: starting in 1 minute`);\n\n // Delay telemetry startup by 1 minute to avoid potential boot loop issues\n setTimeout(() => this.#run(), 60000);\n }\n\n #run() {\n if (this.#stopped) {\n return;\n }\n\n const resource = resourceFromAttributes(this.#getAttributes());\n\n // Add a random jitter to the export interval to avoid all view-syncers exporting at the same time\n const exportIntervalMillis =\n 60000 * this.#viewSyncerCount + Math.floor(Math.random() * 10000);\n const readers = [\n new PeriodicExportingMetricReader({\n exportIntervalMillis,\n exporter: new OTLPMetricExporter({\n url: 'https://metrics.rocicorp.dev',\n timeoutMillis: 30000,\n }),\n }),\n ];\n\n // Uncomment this to debug metrics exports.\n\n // readers.push(\n // new PeriodicExportingMetricReader({\n // exportIntervalMillis,\n // exporter: new ConsoleMetricExporter(),\n // }),\n // );\n\n this.#meterProvider = new MeterProvider({resource, readers});\n this.#meter = this.#meterProvider.getMeter('zero-anonymous-telemetry');\n\n this.#setupMetrics();\n this.#lc?.info?.(\n `telemetry: started (exports every ${exportIntervalMillis / 1000} seconds for ${this.#viewSyncerCount} view-syncers)`,\n );\n }\n\n #setupMetrics() {\n // Observable gauges\n const uptimeGauge = this.#meter.createObservableGauge('zero.uptime', {\n description: 'System uptime in seconds',\n unit: 'seconds',\n });\n\n // Observable counters\n const uptimeCounter = this.#meter.createObservableCounter(\n 'zero.uptime_counter',\n {\n description: 'System uptime in seconds',\n unit: 'seconds',\n },\n );\n const crudMutationsCounter = this.#meter.createObservableCounter(\n 'zero.crud_mutations_processed',\n {\n description: 'Total number of CRUD mutations processed',\n },\n );\n const customMutationsCounter = this.#meter.createObservableCounter(\n 'zero.custom_mutations_processed',\n {\n description: 'Total number of custom mutations processed',\n },\n );\n const totalMutationsCounter = this.#meter.createObservableCounter(\n 'zero.mutations_processed',\n {\n description: 'Total number of mutations processed',\n },\n );\n const crudQueriesCounter = this.#meter.createObservableCounter(\n 'zero.crud_queries_processed',\n {\n description: 'Total number of CRUD queries processed',\n },\n );\n const customQueriesCounter = this.#meter.createObservableCounter(\n 'zero.custom_queries_processed',\n {\n description: 'Total number of custom queries processed',\n },\n );\n const totalQueriesCounter = this.#meter.createObservableCounter(\n 'zero.queries_processed',\n {\n description: 'Total number of queries processed',\n },\n );\n const rowsSyncedCounter = this.#meter.createObservableCounter(\n 'zero.rows_synced',\n {\n description: 'Total number of rows synced',\n },\n );\n\n // Observable counters for connections\n const connectionsSuccessCounter = this.#meter.createObservableCounter(\n 'zero.connections_success',\n {\n description: 'Total number of successful connections',\n },\n );\n\n const connectionsAttemptedCounter = this.#meter.createObservableCounter(\n 'zero.connections_attempted',\n {\n description: 'Total number of attempted connections',\n },\n );\n\n const activeClientGroupsGauge = this.#meter.createObservableGauge(\n 'zero.gauge_active_client_groups',\n {\n description: 'Number of currently active client groups',\n },\n );\n\n const attrs = this.#getAttributes();\n const active =\n (metric: keyof ActiveUsers) => (result: ObservableResult) => {\n const actives = this.#activeUsersGetter?.();\n if (actives) {\n const value = actives[metric];\n result.observe(value, attrs);\n }\n };\n this.#meter\n .createObservableGauge('zero.active_users_last_day', {\n description: 'Count of CVR instances active in the last 24h',\n })\n .addCallback(active('active_users_last_day'));\n this.#meter\n .createObservableGauge('zero.users_1da', {\n description: 'Count of 1-day active profiles',\n })\n .addCallback(active('users_1da'));\n this.#meter\n .createObservableGauge('zero.users_7da', {\n description: 'Count of 7-day active profiles',\n })\n .addCallback(active('users_7da'));\n this.#meter\n .createObservableGauge('zero.users_30da', {\n description: 'Count of 30-day active profiles',\n })\n .addCallback(active('users_30da'));\n this.#meter\n .createObservableGauge('zero.users_1da_legacy', {\n description: 'Count of 1-day active profiles with CVR fallback',\n })\n .addCallback(active('users_1da_legacy'));\n this.#meter\n .createObservableGauge('zero.users_7da_legacy', {\n description: 'Count of 7-day active profiles with CVR fallback',\n })\n .addCallback(active('users_7da_legacy'));\n this.#meter\n .createObservableGauge('zero.users_30da_legacy', {\n description: 'Count of 30-day active profiles with CVR fallback',\n })\n .addCallback(active('users_30da_legacy'));\n\n // Callbacks\n uptimeGauge.addCallback((result: ObservableResult) => {\n const uptimeSeconds = Math.floor(process.uptime());\n result.observe(uptimeSeconds, attrs);\n });\n uptimeCounter.addCallback((result: ObservableResult) => {\n const uptimeSeconds = Math.floor(process.uptime());\n result.observe(uptimeSeconds, attrs);\n });\n crudMutationsCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCrudMutations, attrs);\n });\n customMutationsCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCustomMutations, attrs);\n });\n totalMutationsCounter.addCallback((result: ObservableResult) => {\n const totalMutations =\n this.#totalCrudMutations + this.#totalCustomMutations;\n result.observe(totalMutations, attrs);\n });\n crudQueriesCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCrudQueries, attrs);\n });\n customQueriesCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCustomQueries, attrs);\n });\n totalQueriesCounter.addCallback((result: ObservableResult) => {\n const totalQueries = this.#totalCrudQueries + this.#totalCustomQueries;\n result.observe(totalQueries, attrs);\n });\n rowsSyncedCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalRowsSynced, attrs);\n });\n connectionsSuccessCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalConnectionsSuccess, attrs);\n });\n connectionsAttemptedCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalConnectionsAttempted, attrs);\n });\n activeClientGroupsGauge.addCallback((result: ObservableResult) => {\n const activeClientGroups = this.#activeClientGroupsGetter?.() ?? 0;\n result.observe(activeClientGroups, attrs);\n });\n }\n\n recordMutation(type: 'crud' | 'custom', count = 1) {\n if (type === 'crud') {\n this.#totalCrudMutations += count;\n } else {\n this.#totalCustomMutations += count;\n }\n }\n\n recordQuery(type: 'crud' | 'custom', count = 1) {\n if (type === 'crud') {\n this.#totalCrudQueries += count;\n } else {\n this.#totalCustomQueries += count;\n }\n }\n\n recordRowsSynced(count: number) {\n this.#totalRowsSynced += count;\n }\n\n recordConnectionSuccess() {\n this.#totalConnectionsSuccess++;\n }\n\n recordConnectionAttempted() {\n this.#totalConnectionsAttempted++;\n }\n\n setActiveClientGroupsGetter(getter: () => number) {\n this.#activeClientGroupsGetter = getter;\n }\n\n setActiveUsersGetter(getter: () => ActiveUsers) {\n this.#activeUsersGetter = getter;\n }\n\n shutdown() {\n this.#stopped = true;\n if (this.#meterProvider) {\n this.#lc?.info?.('telemetry: shutting down');\n void this.#meterProvider.shutdown();\n }\n }\n\n #getAttributes() {\n if (!this.#cachedAttributes) {\n this.#cachedAttributes = {\n 'zero.app.id': h64(this.#config?.upstream.db || 'unknown').toString(),\n 'zero.machine.os': platform(),\n 'zero.telemetry.type': 'anonymous',\n 'zero.infra.platform': this.#getPlatform(),\n 'zero.version': getServerVersion(this.#config),\n 'zero.task.id': this.#config?.taskID || 'unknown',\n 'zero.project.id': this.#getGitProjectId(),\n 'zero.process.id': this.#processId,\n 'zero.fs.id': this.#getOrSetFsID(),\n };\n this.#lc?.debug?.(\n `telemetry: cached attributes=${JSON.stringify(this.#cachedAttributes)}`,\n );\n }\n return this.#cachedAttributes;\n }\n\n #getPlatform(): string {\n if (process.env.ZERO_ON_CLOUD_ZERO) return 'cloudzero';\n if (process.env.FLY_APP_NAME || process.env.FLY_REGION) return 'fly.io';\n if (\n process.env.ECS_CONTAINER_METADATA_URI_V4 ||\n process.env.ECS_CONTAINER_METADATA_URI ||\n process.env.AWS_EXECUTION_ENV\n ) {\n return 'aws';\n }\n if (process.env.RAILWAY_ENV || process.env.RAILWAY_STATIC_URL) {\n return 'railway';\n }\n if (process.env.RENDER || process.env.RENDER_SERVICE_ID) return 'render';\n if (\n process.env.GCP_PROJECT ||\n process.env.GCLOUD_PROJECT ||\n process.env.GOOGLE_CLOUD_PROJECT\n ) {\n return 'gcp';\n }\n if (process.env.COOLIFY_URL || process.env.COOLIFY_CONTAINER_NAME) {\n return 'coolify';\n }\n if (process.env.CONTAINER_APP_REVISION) return 'azure';\n if (process.env.FLIGHTCONTROL || process.env.FC_URL) return 'flightcontrol';\n return 'unknown';\n }\n\n #findUp(startDir: string, target: string): string | null {\n let dir = startDir;\n while (dir !== dirname(dir)) {\n if (existsSync(join(dir, target))) return dir;\n dir = dirname(dir);\n }\n return null;\n }\n\n #getGitProjectId(): string {\n try {\n const cwd = process.cwd();\n const gitRoot = this.#findUp(cwd, '.git');\n if (!gitRoot) {\n return 'unknown';\n }\n\n const rootCommitHash = execSync('git rev-list --max-parents=0 HEAD -1', {\n cwd: gitRoot,\n encoding: 'utf8',\n timeout: 1000,\n stdio: ['ignore', 'pipe', 'ignore'], // Suppress stderr\n }).trim();\n\n return rootCommitHash.length === 40 ? rootCommitHash : 'unknown';\n } catch (error) {\n // execSync throws a child_process.SpawnSyncReturns-shaped error with an\n // output property (an array with stdio buffers) and sometimes a killed\n // property that references back to the error itself — making it\n // circular and causing stringify() in the Logger logic to throw.\n const details =\n error instanceof Error\n ? {message: error.message, name: error.name, stack: error.stack}\n : String(error);\n this.#lc?.debug?.('telemetry: unable to get Git root commit:', details);\n return 'unknown';\n }\n }\n\n #getOrSetFsID(): string {\n try {\n if (this.#isInContainer()) {\n return 'container';\n }\n const fsidPath = join(homedir(), '.rocicorp', 'fsid');\n const fsidDir = dirname(fsidPath);\n\n mkdirSync(fsidDir, {recursive: true});\n\n // Always try atomic file creation first - this eliminates any race conditions\n const newId = randomUUID();\n try {\n writeFileSync(fsidPath, newId, {encoding: 'utf8', flag: 'wx'});\n return newId;\n } catch (writeError) {\n if ((writeError as NodeJS.ErrnoException).code === 'EEXIST') {\n const existingId = readFileSync(fsidPath, 'utf8').trim();\n return existingId;\n }\n throw writeError;\n }\n } catch (error) {\n this.#lc?.debug?.(\n 'telemetry: unable to get or set filesystem ID:',\n error,\n );\n return 'unknown';\n }\n }\n\n #isInContainer(): boolean {\n try {\n if (process.env.ZERO_IN_CONTAINER) {\n return true;\n }\n\n if (existsSync('/.dockerenv')) {\n return true;\n }\n\n if (existsSync('/usr/local/bin/docker-entrypoint.sh')) {\n return true;\n }\n\n if (process.env.KUBERNETES_SERVICE_HOST) {\n return true;\n }\n\n if (\n process.env.DOCKER_CONTAINER_ID ||\n process.env.HOSTNAME?.match(/^[a-f0-9]{12}$/)\n ) {\n return true;\n }\n\n if (existsSync('/proc/1/cgroup')) {\n const cgroup = readFileSync('/proc/1/cgroup', 'utf8');\n if (\n cgroup.includes('docker') ||\n cgroup.includes('kubepods') ||\n cgroup.includes('containerd')\n ) {\n return true;\n }\n }\n\n return false;\n } catch (error) {\n this.#lc?.debug?.(\n 'telemetry: unable to detect container environment:',\n error,\n );\n return false;\n }\n }\n}\n\nconst manager = () => AnonymousTelemetryManager.getInstance();\n\nexport const startAnonymousTelemetry = (lc?: LogContext, config?: ZeroConfig) =>\n manager().start(lc, config);\nexport const recordMutation = (type: 'crud' | 'custom', count = 1) =>\n manager().recordMutation(type, count);\nexport const recordQuery = (type: 'crud' | 'custom', count = 1) =>\n manager().recordQuery(type, count);\nexport const recordRowsSynced = (count: number) =>\n manager().recordRowsSynced(count);\nexport const recordConnectionSuccess = () =>\n manager().recordConnectionSuccess();\nexport const recordConnectionAttempted = () =>\n manager().recordConnectionAttempted();\nexport const setActiveClientGroupsGetter = (getter: () => number) =>\n manager().setActiveClientGroupsGetter(getter);\nexport const setActiveUsersGetter = (getter: () => ActiveUsers) =>\n manager().setActiveUsersGetter(getter);\nexport const shutdownAnonymousTelemetry = () => manager().shutdown();\n"],"mappings":"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"}
1
+ {"version":3,"file":"anonymous-otel-start.js","names":["#instance","#processId","#lc","#starting","#config","#viewSyncerCount","#cachedAttributes","#run","#stopped","#getAttributes","#meterProvider","#meter","#setupMetrics","#activeUsersGetter","#totalCrudMutations","#totalCustomMutations","#totalCrudQueries","#totalCustomQueries","#totalRowsSynced","#totalConnectionsSuccess","#totalConnectionsAttempted","#activeClientGroupsGetter","#getPlatform","#getGitProjectId","#getOrSetFsID","#findUp","#isInContainer"],"sources":["../../../../../zero-cache/src/server/anonymous-otel-start.ts"],"sourcesContent":["import type {ObservableResult} from '@opentelemetry/api';\nimport {type Meter} from '@opentelemetry/api';\nimport {OTLPMetricExporter} from '@opentelemetry/exporter-metrics-otlp-http';\nimport {resourceFromAttributes} from '@opentelemetry/resources';\nimport {\n MeterProvider,\n PeriodicExportingMetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport type {LogContext} from '@rocicorp/logger';\nimport {execSync} from 'child_process';\nimport {randomUUID} from 'crypto';\nimport {existsSync, mkdirSync, readFileSync, writeFileSync} from 'fs';\nimport {homedir, platform} from 'os';\nimport {dirname, join} from 'path';\nimport {h64} from '../../../shared/src/hash.js';\nimport {\n getServerVersion,\n getZeroConfig,\n type ZeroConfig,\n} from '../config/zero-config.js';\nimport {setupOtelDiagnosticLogger} from './otel-diag-logger.ts';\n\nexport type ActiveUsers = {\n active_users_last_day: number;\n users_1da: number;\n users_7da: number;\n users_30da: number;\n users_1da_legacy: number;\n users_7da_legacy: number;\n users_30da_legacy: number;\n};\n\nconst hostNameRe = /^[a-f0-9]{12}$/;\n\nclass AnonymousTelemetryManager {\n static #instance: AnonymousTelemetryManager;\n #starting = false;\n #stopped = false;\n #meter!: Meter;\n #meterProvider!: MeterProvider;\n #totalCrudMutations = 0;\n #totalCustomMutations = 0;\n #totalCrudQueries = 0;\n #totalCustomQueries = 0;\n #totalRowsSynced = 0;\n #totalConnectionsSuccess = 0;\n #totalConnectionsAttempted = 0;\n #activeClientGroupsGetter: (() => number) | undefined;\n #activeUsersGetter: (() => ActiveUsers) | undefined;\n #lc: LogContext | undefined;\n #config: ZeroConfig | undefined;\n #processId: string;\n #cachedAttributes: Record<string, string> | undefined;\n #viewSyncerCount = 1;\n\n private constructor() {\n this.#processId = randomUUID();\n }\n\n static getInstance(): AnonymousTelemetryManager {\n if (!AnonymousTelemetryManager.#instance) {\n AnonymousTelemetryManager.#instance = new AnonymousTelemetryManager();\n }\n return AnonymousTelemetryManager.#instance;\n }\n\n start(lc?: LogContext, config?: ZeroConfig) {\n this.#lc = lc;\n\n // Set up OpenTelemetry diagnostic logger if not already configured\n setupOtelDiagnosticLogger(lc);\n\n if (!config) {\n try {\n config = getZeroConfig();\n } catch (e) {\n this.#lc?.info?.('telemetry: disabled - unable to parse config', e);\n return;\n }\n }\n\n if (process.env.DO_NOT_TRACK) {\n this.#lc?.info?.(\n 'telemetry: disabled - DO_NOT_TRACK environment variable is set',\n );\n return;\n }\n\n if (!config.enableTelemetry) {\n this.#lc?.info?.('telemetry: disabled - enableTelemetry is false');\n return;\n }\n\n if (this.#starting) {\n return;\n }\n\n this.#starting = true;\n this.#config = config;\n this.#viewSyncerCount = config.numSyncWorkers ?? 1;\n this.#cachedAttributes = undefined;\n\n this.#lc?.info?.(`telemetry: starting in 1 minute`);\n\n // Delay telemetry startup by 1 minute to avoid potential boot loop issues\n setTimeout(() => this.#run(), 60000);\n }\n\n #run() {\n if (this.#stopped) {\n return;\n }\n\n const resource = resourceFromAttributes(this.#getAttributes());\n\n // Add a random jitter to the export interval to avoid all view-syncers exporting at the same time\n const exportIntervalMillis =\n 60000 * this.#viewSyncerCount + Math.floor(Math.random() * 10000);\n const readers = [\n new PeriodicExportingMetricReader({\n exportIntervalMillis,\n exporter: new OTLPMetricExporter({\n url: 'https://metrics.rocicorp.dev',\n timeoutMillis: 30000,\n }),\n }),\n ];\n\n // Uncomment this to debug metrics exports.\n\n // readers.push(\n // new PeriodicExportingMetricReader({\n // exportIntervalMillis,\n // exporter: new ConsoleMetricExporter(),\n // }),\n // );\n\n this.#meterProvider = new MeterProvider({resource, readers});\n this.#meter = this.#meterProvider.getMeter('zero-anonymous-telemetry');\n\n this.#setupMetrics();\n this.#lc?.info?.(\n `telemetry: started (exports every ${exportIntervalMillis / 1000} seconds for ${this.#viewSyncerCount} view-syncers)`,\n );\n }\n\n #setupMetrics() {\n // Observable gauges\n const uptimeGauge = this.#meter.createObservableGauge('zero.uptime', {\n description: 'System uptime in seconds',\n unit: 'seconds',\n });\n\n // Observable counters\n const uptimeCounter = this.#meter.createObservableCounter(\n 'zero.uptime_counter',\n {\n description: 'System uptime in seconds',\n unit: 'seconds',\n },\n );\n const crudMutationsCounter = this.#meter.createObservableCounter(\n 'zero.crud_mutations_processed',\n {\n description: 'Total number of CRUD mutations processed',\n },\n );\n const customMutationsCounter = this.#meter.createObservableCounter(\n 'zero.custom_mutations_processed',\n {\n description: 'Total number of custom mutations processed',\n },\n );\n const totalMutationsCounter = this.#meter.createObservableCounter(\n 'zero.mutations_processed',\n {\n description: 'Total number of mutations processed',\n },\n );\n const crudQueriesCounter = this.#meter.createObservableCounter(\n 'zero.crud_queries_processed',\n {\n description: 'Total number of CRUD queries processed',\n },\n );\n const customQueriesCounter = this.#meter.createObservableCounter(\n 'zero.custom_queries_processed',\n {\n description: 'Total number of custom queries processed',\n },\n );\n const totalQueriesCounter = this.#meter.createObservableCounter(\n 'zero.queries_processed',\n {\n description: 'Total number of queries processed',\n },\n );\n const rowsSyncedCounter = this.#meter.createObservableCounter(\n 'zero.rows_synced',\n {\n description: 'Total number of rows synced',\n },\n );\n\n // Observable counters for connections\n const connectionsSuccessCounter = this.#meter.createObservableCounter(\n 'zero.connections_success',\n {\n description: 'Total number of successful connections',\n },\n );\n\n const connectionsAttemptedCounter = this.#meter.createObservableCounter(\n 'zero.connections_attempted',\n {\n description: 'Total number of attempted connections',\n },\n );\n\n const activeClientGroupsGauge = this.#meter.createObservableGauge(\n 'zero.gauge_active_client_groups',\n {\n description: 'Number of currently active client groups',\n },\n );\n\n const attrs = this.#getAttributes();\n const active =\n (metric: keyof ActiveUsers) => (result: ObservableResult) => {\n const actives = this.#activeUsersGetter?.();\n if (actives) {\n const value = actives[metric];\n result.observe(value, attrs);\n }\n };\n this.#meter\n .createObservableGauge('zero.active_users_last_day', {\n description: 'Count of CVR instances active in the last 24h',\n })\n .addCallback(active('active_users_last_day'));\n this.#meter\n .createObservableGauge('zero.users_1da', {\n description: 'Count of 1-day active profiles',\n })\n .addCallback(active('users_1da'));\n this.#meter\n .createObservableGauge('zero.users_7da', {\n description: 'Count of 7-day active profiles',\n })\n .addCallback(active('users_7da'));\n this.#meter\n .createObservableGauge('zero.users_30da', {\n description: 'Count of 30-day active profiles',\n })\n .addCallback(active('users_30da'));\n this.#meter\n .createObservableGauge('zero.users_1da_legacy', {\n description: 'Count of 1-day active profiles with CVR fallback',\n })\n .addCallback(active('users_1da_legacy'));\n this.#meter\n .createObservableGauge('zero.users_7da_legacy', {\n description: 'Count of 7-day active profiles with CVR fallback',\n })\n .addCallback(active('users_7da_legacy'));\n this.#meter\n .createObservableGauge('zero.users_30da_legacy', {\n description: 'Count of 30-day active profiles with CVR fallback',\n })\n .addCallback(active('users_30da_legacy'));\n\n // Callbacks\n uptimeGauge.addCallback((result: ObservableResult) => {\n const uptimeSeconds = Math.floor(process.uptime());\n result.observe(uptimeSeconds, attrs);\n });\n uptimeCounter.addCallback((result: ObservableResult) => {\n const uptimeSeconds = Math.floor(process.uptime());\n result.observe(uptimeSeconds, attrs);\n });\n crudMutationsCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCrudMutations, attrs);\n });\n customMutationsCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCustomMutations, attrs);\n });\n totalMutationsCounter.addCallback((result: ObservableResult) => {\n const totalMutations =\n this.#totalCrudMutations + this.#totalCustomMutations;\n result.observe(totalMutations, attrs);\n });\n crudQueriesCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCrudQueries, attrs);\n });\n customQueriesCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalCustomQueries, attrs);\n });\n totalQueriesCounter.addCallback((result: ObservableResult) => {\n const totalQueries = this.#totalCrudQueries + this.#totalCustomQueries;\n result.observe(totalQueries, attrs);\n });\n rowsSyncedCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalRowsSynced, attrs);\n });\n connectionsSuccessCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalConnectionsSuccess, attrs);\n });\n connectionsAttemptedCounter.addCallback((result: ObservableResult) => {\n result.observe(this.#totalConnectionsAttempted, attrs);\n });\n activeClientGroupsGauge.addCallback((result: ObservableResult) => {\n const activeClientGroups = this.#activeClientGroupsGetter?.() ?? 0;\n result.observe(activeClientGroups, attrs);\n });\n }\n\n recordMutation(type: 'crud' | 'custom', count = 1) {\n if (type === 'crud') {\n this.#totalCrudMutations += count;\n } else {\n this.#totalCustomMutations += count;\n }\n }\n\n recordQuery(type: 'crud' | 'custom', count = 1) {\n if (type === 'crud') {\n this.#totalCrudQueries += count;\n } else {\n this.#totalCustomQueries += count;\n }\n }\n\n recordRowsSynced(count: number) {\n this.#totalRowsSynced += count;\n }\n\n recordConnectionSuccess() {\n this.#totalConnectionsSuccess++;\n }\n\n recordConnectionAttempted() {\n this.#totalConnectionsAttempted++;\n }\n\n setActiveClientGroupsGetter(getter: () => number) {\n this.#activeClientGroupsGetter = getter;\n }\n\n setActiveUsersGetter(getter: () => ActiveUsers) {\n this.#activeUsersGetter = getter;\n }\n\n shutdown() {\n this.#stopped = true;\n if (this.#meterProvider) {\n this.#lc?.info?.('telemetry: shutting down');\n void this.#meterProvider.shutdown();\n }\n }\n\n #getAttributes() {\n if (!this.#cachedAttributes) {\n this.#cachedAttributes = {\n 'zero.app.id': h64(this.#config?.upstream.db || 'unknown').toString(),\n 'zero.machine.os': platform(),\n 'zero.telemetry.type': 'anonymous',\n 'zero.infra.platform': this.#getPlatform(),\n 'zero.version': getServerVersion(this.#config),\n 'zero.task.id': this.#config?.taskID || 'unknown',\n 'zero.project.id': this.#getGitProjectId(),\n 'zero.process.id': this.#processId,\n 'zero.fs.id': this.#getOrSetFsID(),\n };\n this.#lc?.debug?.(\n `telemetry: cached attributes=${JSON.stringify(this.#cachedAttributes)}`,\n );\n }\n return this.#cachedAttributes;\n }\n\n #getPlatform(): string {\n if (process.env.ZERO_ON_CLOUD_ZERO) return 'cloudzero';\n if (process.env.FLY_APP_NAME || process.env.FLY_REGION) return 'fly.io';\n if (\n process.env.ECS_CONTAINER_METADATA_URI_V4 ||\n process.env.ECS_CONTAINER_METADATA_URI ||\n process.env.AWS_EXECUTION_ENV\n ) {\n return 'aws';\n }\n if (process.env.RAILWAY_ENV || process.env.RAILWAY_STATIC_URL) {\n return 'railway';\n }\n if (process.env.RENDER || process.env.RENDER_SERVICE_ID) return 'render';\n if (\n process.env.GCP_PROJECT ||\n process.env.GCLOUD_PROJECT ||\n process.env.GOOGLE_CLOUD_PROJECT\n ) {\n return 'gcp';\n }\n if (process.env.COOLIFY_URL || process.env.COOLIFY_CONTAINER_NAME) {\n return 'coolify';\n }\n if (process.env.CONTAINER_APP_REVISION) return 'azure';\n if (process.env.FLIGHTCONTROL || process.env.FC_URL) return 'flightcontrol';\n return 'unknown';\n }\n\n #findUp(startDir: string, target: string): string | null {\n let dir = startDir;\n while (dir !== dirname(dir)) {\n if (existsSync(join(dir, target))) return dir;\n dir = dirname(dir);\n }\n return null;\n }\n\n #getGitProjectId(): string {\n try {\n const cwd = process.cwd();\n const gitRoot = this.#findUp(cwd, '.git');\n if (!gitRoot) {\n return 'unknown';\n }\n\n const rootCommitHash = execSync('git rev-list --max-parents=0 HEAD -1', {\n cwd: gitRoot,\n encoding: 'utf8',\n timeout: 1000,\n stdio: ['ignore', 'pipe', 'ignore'], // Suppress stderr\n }).trim();\n\n return rootCommitHash.length === 40 ? rootCommitHash : 'unknown';\n } catch (error) {\n // execSync throws a child_process.SpawnSyncReturns-shaped error with an\n // output property (an array with stdio buffers) and sometimes a killed\n // property that references back to the error itself — making it\n // circular and causing stringify() in the Logger logic to throw.\n const details =\n error instanceof Error\n ? {message: error.message, name: error.name, stack: error.stack}\n : String(error);\n this.#lc?.debug?.('telemetry: unable to get Git root commit:', details);\n return 'unknown';\n }\n }\n\n #getOrSetFsID(): string {\n try {\n if (this.#isInContainer()) {\n return 'container';\n }\n const fsidPath = join(homedir(), '.rocicorp', 'fsid');\n const fsidDir = dirname(fsidPath);\n\n mkdirSync(fsidDir, {recursive: true});\n\n // Always try atomic file creation first - this eliminates any race conditions\n const newId = randomUUID();\n try {\n writeFileSync(fsidPath, newId, {encoding: 'utf8', flag: 'wx'});\n return newId;\n } catch (writeError) {\n if ((writeError as NodeJS.ErrnoException).code === 'EEXIST') {\n const existingId = readFileSync(fsidPath, 'utf8').trim();\n return existingId;\n }\n throw writeError;\n }\n } catch (error) {\n this.#lc?.debug?.(\n 'telemetry: unable to get or set filesystem ID:',\n error,\n );\n return 'unknown';\n }\n }\n\n #isInContainer(): boolean {\n try {\n if (process.env.ZERO_IN_CONTAINER) {\n return true;\n }\n\n if (existsSync('/.dockerenv')) {\n return true;\n }\n\n if (existsSync('/usr/local/bin/docker-entrypoint.sh')) {\n return true;\n }\n\n if (process.env.KUBERNETES_SERVICE_HOST) {\n return true;\n }\n\n if (\n process.env.DOCKER_CONTAINER_ID ||\n process.env.HOSTNAME?.match(hostNameRe)\n ) {\n return true;\n }\n\n if (existsSync('/proc/1/cgroup')) {\n const cgroup = readFileSync('/proc/1/cgroup', 'utf8');\n if (\n cgroup.includes('docker') ||\n cgroup.includes('kubepods') ||\n cgroup.includes('containerd')\n ) {\n return true;\n }\n }\n\n return false;\n } catch (error) {\n this.#lc?.debug?.(\n 'telemetry: unable to detect container environment:',\n error,\n );\n return false;\n }\n }\n}\n\nconst manager = () => AnonymousTelemetryManager.getInstance();\n\nexport const startAnonymousTelemetry = (lc?: LogContext, config?: ZeroConfig) =>\n manager().start(lc, config);\nexport const recordMutation = (type: 'crud' | 'custom', count = 1) =>\n manager().recordMutation(type, count);\nexport const recordQuery = (type: 'crud' | 'custom', count = 1) =>\n manager().recordQuery(type, count);\nexport const recordRowsSynced = (count: number) =>\n manager().recordRowsSynced(count);\nexport const recordConnectionSuccess = () =>\n manager().recordConnectionSuccess();\nexport const recordConnectionAttempted = () =>\n manager().recordConnectionAttempted();\nexport const setActiveClientGroupsGetter = (getter: () => number) =>\n manager().setActiveClientGroupsGetter(getter);\nexport const setActiveUsersGetter = (getter: () => ActiveUsers) =>\n manager().setActiveUsersGetter(getter);\nexport const shutdownAnonymousTelemetry = () => manager().shutdown();\n"],"mappings":"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"}
@@ -20,7 +20,7 @@ async function runWorker(parent, env) {
20
20
  initEventSink(lc, config);
21
21
  const processes = new ProcessManager(lc, parent);
22
22
  const { numSyncWorkers: numSyncers } = config;
23
- if (config.upstream.maxConns < numSyncers) throw new Error(`Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`);
23
+ if (config.enableCrudMutations && config.upstream.maxConns < numSyncers) throw new Error(`Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`);
24
24
  if (config.cvr.maxConns < numSyncers) throw new Error(`Insufficient cvr connections (${config.cvr.maxConns}) for ${numSyncers} syncers.Increase ZERO_CVR_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`);
25
25
  const internalFlags = numSyncers === 0 ? [] : [
26
26
  "--upstream-max-conns-per-worker",
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","names":[],"sources":["../../../../../zero-cache/src/server/main.ts"],"sourcesContent":["import {resolver} from '@rocicorp/resolver';\nimport path from 'node:path';\nimport {must} from '../../../shared/src/must.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {\n exitAfter,\n ProcessManager,\n runUntilKilled,\n type WorkerType,\n} from '../services/life-cycle.ts';\nimport {\n restoreReplica,\n startReplicaBackupProcess,\n} from '../services/litestream/commands.ts';\nimport {\n childWorker,\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {\n createNotifierFrom,\n handleSubscriptionsFrom,\n type ReplicaFileMode,\n subscribeTo,\n} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {WorkerDispatcher} from './worker-dispatcher.ts';\nimport {\n CHANGE_STREAMER_URL,\n MUTATOR_URL,\n REAPER_URL,\n REPLICATOR_URL,\n SYNCER_URL,\n} from './worker-urls.ts';\n\nconst clientConnectionBifurcated = false;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n const startMs = Date.now();\n const config = getNormalizedZeroConfig({env});\n\n startOtelAuto(createLogContext(config, {worker: 'dispatcher'}, false));\n const lc = createLogContext(config, {worker: 'dispatcher'}, true);\n initEventSink(lc, config);\n\n const processes = new ProcessManager(lc, parent);\n\n const {numSyncWorkers: numSyncers} = config;\n if (config.upstream.maxConns < numSyncers) {\n throw new Error(\n `Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n if (config.cvr.maxConns < numSyncers) {\n throw new Error(\n `Insufficient cvr connections (${config.cvr.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_CVR_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n\n const internalFlags: string[] =\n numSyncers === 0\n ? []\n : [\n '--upstream-max-conns-per-worker',\n String(Math.floor(config.upstream.maxConns / numSyncers)),\n '--cvr-max-conns-per-worker',\n String(Math.floor(config.cvr.maxConns / numSyncers)),\n ];\n\n function loadWorker(\n moduleUrl: URL,\n type: WorkerType,\n id?: string | number,\n ...args: string[]\n ): Worker {\n const worker = childWorker(moduleUrl, env, ...args, ...internalFlags);\n const name = path.basename(moduleUrl.pathname) + (id ? ` (${id})` : '');\n return processes.addWorker(worker, type, name);\n }\n\n const {\n taskID,\n changeStreamer: {mode: changeStreamerMode, uri: changeStreamerURI},\n litestream,\n } = config;\n const runChangeStreamer =\n changeStreamerMode === 'dedicated' && changeStreamerURI === undefined;\n\n let restoreStart = new Date();\n if (litestream.backupURL || (litestream.executable && !runChangeStreamer)) {\n try {\n restoreStart = await restoreReplica(lc, config);\n } catch (e) {\n if (runChangeStreamer) {\n // If the restore failed, e.g. due to a corrupt backup, the\n // replication-manager recovers by re-syncing.\n lc.error?.('error restoring backup. resyncing the replica.', e);\n } else {\n // View-syncers, on the other hand, have no option other than to retry\n // until a valid backup has been published. This is achieved by\n // shutting down and letting the container runner retry with its\n // configured policy.\n throw e;\n }\n }\n }\n\n const {promise: changeStreamerReady, resolve: changeStreamerStarted} =\n resolver();\n const changeStreamer = runChangeStreamer\n ? loadWorker(\n CHANGE_STREAMER_URL,\n 'supporting',\n undefined,\n String(restoreStart.getTime()),\n ).once('message', changeStreamerStarted)\n : (changeStreamerStarted() ?? undefined);\n\n const {promise: reaperReady, resolve: reaperStarted} = resolver();\n if (numSyncers > 0) {\n loadWorker(REAPER_URL, 'supporting').once('message', reaperStarted);\n } else {\n reaperStarted();\n }\n\n // Wait for the change-streamer to be ready to guarantee that a replica\n // file is present.\n await changeStreamerReady;\n\n if (runChangeStreamer && litestream.backupURL) {\n // Start a backup replicator and corresponding litestream backup process.\n const {promise: backupReady, resolve} = resolver();\n const mode: ReplicaFileMode = 'backup';\n loadWorker(REPLICATOR_URL, 'supporting', mode, mode).once(\n // Wait for the Replicator's first message (i.e. \"ready\") before starting\n // litestream backup in order to avoid contending on the lock when the\n // replicator first prepares the db file.\n 'message',\n () => {\n processes.addSubprocess(\n startReplicaBackupProcess(lc, config),\n 'supporting',\n 'litestream',\n );\n resolve();\n },\n );\n await backupReady;\n }\n\n // Before starting the view-syncers, ensure that the reaper has started\n // up, indicating that any CVR db migrations have been performed.\n await reaperReady;\n\n const syncers: Worker[] = [];\n if (numSyncers) {\n const mode: ReplicaFileMode =\n runChangeStreamer && litestream.backupURL ? 'serving-copy' : 'serving';\n const {promise: replicaReady, resolve} = resolver();\n const replicator = loadWorker(\n REPLICATOR_URL,\n 'supporting',\n mode,\n mode,\n ).once('message', () => {\n subscribeTo(lc, replicator);\n resolve();\n });\n await replicaReady;\n\n const notifier = createNotifierFrom(lc, replicator);\n for (let i = 0; i < numSyncers; i++) {\n syncers.push(loadWorker(SYNCER_URL, 'user-facing', i + 1, mode));\n }\n syncers.forEach(syncer => handleSubscriptionsFrom(lc, syncer, notifier));\n }\n let mutator: Worker | undefined;\n if (clientConnectionBifurcated) {\n mutator = loadWorker(MUTATOR_URL, 'supporting', 'mutator');\n }\n\n lc.info?.('waiting for workers to be ready ...');\n const logWaiting = setInterval(\n () => lc.info?.(`still waiting for ${processes.initializing().join(', ')}`),\n 10_000,\n );\n await processes.allWorkersReady();\n clearInterval(logWaiting);\n lc.info?.(`all workers ready (${Date.now() - startMs} ms)`);\n\n parent.send(['ready', {ready: true}]);\n\n try {\n await runUntilKilled(\n lc,\n parent,\n new WorkerDispatcher(\n lc,\n taskID,\n parent,\n syncers,\n mutator,\n changeStreamer,\n ),\n );\n } catch (err) {\n processes.logErrorAndExit(err, 'dispatcher');\n }\n\n await processes.done();\n}\n\nif (!singleProcessMode()) {\n void exitAfter(() => runWorker(must(parentWorker), process.env));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"main.js","names":[],"sources":["../../../../../zero-cache/src/server/main.ts"],"sourcesContent":["import {resolver} from '@rocicorp/resolver';\nimport path from 'node:path';\nimport {must} from '../../../shared/src/must.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {\n exitAfter,\n ProcessManager,\n runUntilKilled,\n type WorkerType,\n} from '../services/life-cycle.ts';\nimport {\n restoreReplica,\n startReplicaBackupProcess,\n} from '../services/litestream/commands.ts';\nimport {\n childWorker,\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {\n createNotifierFrom,\n handleSubscriptionsFrom,\n type ReplicaFileMode,\n subscribeTo,\n} from '../workers/replicator.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {WorkerDispatcher} from './worker-dispatcher.ts';\nimport {\n CHANGE_STREAMER_URL,\n MUTATOR_URL,\n REAPER_URL,\n REPLICATOR_URL,\n SYNCER_URL,\n} from './worker-urls.ts';\n\nconst clientConnectionBifurcated = false;\n\nexport default async function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n): Promise<void> {\n const startMs = Date.now();\n const config = getNormalizedZeroConfig({env});\n\n startOtelAuto(createLogContext(config, {worker: 'dispatcher'}, false));\n const lc = createLogContext(config, {worker: 'dispatcher'}, true);\n initEventSink(lc, config);\n\n const processes = new ProcessManager(lc, parent);\n\n const {numSyncWorkers: numSyncers} = config;\n if (config.enableCrudMutations && config.upstream.maxConns < numSyncers) {\n throw new Error(\n `Insufficient upstream connections (${config.upstream.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_UPSTREAM_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n if (config.cvr.maxConns < numSyncers) {\n throw new Error(\n `Insufficient cvr connections (${config.cvr.maxConns}) for ${numSyncers} syncers.` +\n `Increase ZERO_CVR_MAX_CONNS or decrease ZERO_NUM_SYNC_WORKERS (which defaults to available cores).`,\n );\n }\n\n const internalFlags: string[] =\n numSyncers === 0\n ? []\n : [\n '--upstream-max-conns-per-worker',\n String(Math.floor(config.upstream.maxConns / numSyncers)),\n '--cvr-max-conns-per-worker',\n String(Math.floor(config.cvr.maxConns / numSyncers)),\n ];\n\n function loadWorker(\n moduleUrl: URL,\n type: WorkerType,\n id?: string | number,\n ...args: string[]\n ): Worker {\n const worker = childWorker(moduleUrl, env, ...args, ...internalFlags);\n const name = path.basename(moduleUrl.pathname) + (id ? ` (${id})` : '');\n return processes.addWorker(worker, type, name);\n }\n\n const {\n taskID,\n changeStreamer: {mode: changeStreamerMode, uri: changeStreamerURI},\n litestream,\n } = config;\n const runChangeStreamer =\n changeStreamerMode === 'dedicated' && changeStreamerURI === undefined;\n\n let restoreStart = new Date();\n if (litestream.backupURL || (litestream.executable && !runChangeStreamer)) {\n try {\n restoreStart = await restoreReplica(lc, config);\n } catch (e) {\n if (runChangeStreamer) {\n // If the restore failed, e.g. due to a corrupt backup, the\n // replication-manager recovers by re-syncing.\n lc.error?.('error restoring backup. resyncing the replica.', e);\n } else {\n // View-syncers, on the other hand, have no option other than to retry\n // until a valid backup has been published. This is achieved by\n // shutting down and letting the container runner retry with its\n // configured policy.\n throw e;\n }\n }\n }\n\n const {promise: changeStreamerReady, resolve: changeStreamerStarted} =\n resolver();\n const changeStreamer = runChangeStreamer\n ? loadWorker(\n CHANGE_STREAMER_URL,\n 'supporting',\n undefined,\n String(restoreStart.getTime()),\n ).once('message', changeStreamerStarted)\n : (changeStreamerStarted() ?? undefined);\n\n const {promise: reaperReady, resolve: reaperStarted} = resolver();\n if (numSyncers > 0) {\n loadWorker(REAPER_URL, 'supporting').once('message', reaperStarted);\n } else {\n reaperStarted();\n }\n\n // Wait for the change-streamer to be ready to guarantee that a replica\n // file is present.\n await changeStreamerReady;\n\n if (runChangeStreamer && litestream.backupURL) {\n // Start a backup replicator and corresponding litestream backup process.\n const {promise: backupReady, resolve} = resolver();\n const mode: ReplicaFileMode = 'backup';\n loadWorker(REPLICATOR_URL, 'supporting', mode, mode).once(\n // Wait for the Replicator's first message (i.e. \"ready\") before starting\n // litestream backup in order to avoid contending on the lock when the\n // replicator first prepares the db file.\n 'message',\n () => {\n processes.addSubprocess(\n startReplicaBackupProcess(lc, config),\n 'supporting',\n 'litestream',\n );\n resolve();\n },\n );\n await backupReady;\n }\n\n // Before starting the view-syncers, ensure that the reaper has started\n // up, indicating that any CVR db migrations have been performed.\n await reaperReady;\n\n const syncers: Worker[] = [];\n if (numSyncers) {\n const mode: ReplicaFileMode =\n runChangeStreamer && litestream.backupURL ? 'serving-copy' : 'serving';\n const {promise: replicaReady, resolve} = resolver();\n const replicator = loadWorker(\n REPLICATOR_URL,\n 'supporting',\n mode,\n mode,\n ).once('message', () => {\n subscribeTo(lc, replicator);\n resolve();\n });\n await replicaReady;\n\n const notifier = createNotifierFrom(lc, replicator);\n for (let i = 0; i < numSyncers; i++) {\n syncers.push(loadWorker(SYNCER_URL, 'user-facing', i + 1, mode));\n }\n syncers.forEach(syncer => handleSubscriptionsFrom(lc, syncer, notifier));\n }\n let mutator: Worker | undefined;\n if (clientConnectionBifurcated) {\n mutator = loadWorker(MUTATOR_URL, 'supporting', 'mutator');\n }\n\n lc.info?.('waiting for workers to be ready ...');\n const logWaiting = setInterval(\n () => lc.info?.(`still waiting for ${processes.initializing().join(', ')}`),\n 10_000,\n );\n await processes.allWorkersReady();\n clearInterval(logWaiting);\n lc.info?.(`all workers ready (${Date.now() - startMs} ms)`);\n\n parent.send(['ready', {ready: true}]);\n\n try {\n await runUntilKilled(\n lc,\n parent,\n new WorkerDispatcher(\n lc,\n taskID,\n parent,\n syncers,\n mutator,\n changeStreamer,\n ),\n );\n } catch (err) {\n processes.logErrorAndExit(err, 'dispatcher');\n }\n\n await processes.done();\n}\n\nif (!singleProcessMode()) {\n void exitAfter(() => runWorker(must(parentWorker), process.env));\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/syncer.ts"],"names":[],"mappings":"AAwBA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AA8B/B,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CAwJf"}
1
+ {"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/syncer.ts"],"names":[],"mappings":"AAyBA,OAAO,EAGL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AA8B/B,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,GAAG,IAAI,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,IAAI,CAAC,CA+Jf"}
@@ -1,4 +1,5 @@
1
1
  import { assert } from "../../../shared/src/asserts.js";
2
+ import { promiseVoid } from "../../../shared/src/resolved-promises.js";
2
3
  import { parse } from "../../../shared/src/valita.js";
3
4
  import { must } from "../../../shared/src/must.js";
4
5
  import { parentWorker, singleProcessMode } from "../types/processes.js";
@@ -50,20 +51,18 @@ function runWorker(parent, env, ...args) {
50
51
  initEventSink(lc, config);
51
52
  assert(args.length > 0, `replicator mode not specified`);
52
53
  const fileMode = parse(args[0], replicaFileModeSchema);
53
- const { cvr, upstream } = config;
54
- assert(cvr.maxConnsPerWorker, "cvr.maxConnsPerWorker must be set");
55
- assert(upstream.maxConnsPerWorker, "upstream.maxConnsPerWorker must be set");
54
+ const { cvr, upstream, enableCrudMutations } = config;
56
55
  const replicaFile = replicaFileName(config.replica.file, fileMode);
57
56
  lc.debug?.(`running view-syncer on ${replicaFile}`);
58
57
  const cvrDB = pgClient(lc, cvr.db, {
59
- max: cvr.maxConnsPerWorker,
58
+ max: must(cvr.maxConnsPerWorker, "cvr.maxConnsPerWorker must be set"),
60
59
  connection: { ["application_name"]: `zero-sync-worker-${pid}-cvr` }
61
60
  });
62
- const upstreamDB = pgClient(lc, upstream.db, {
63
- max: upstream.maxConnsPerWorker,
61
+ const upstreamDB = enableCrudMutations ? pgClient(lc, upstream.db, {
62
+ max: must(upstream.maxConnsPerWorker, "upstream.maxConnsPerWorker must be set"),
64
63
  connection: { ["application_name"]: `zero-sync-worker-${pid}-upstream` }
65
- });
66
- const dbWarmup = Promise.allSettled([warmupConnections(lc, cvrDB, "cvr"), warmupConnections(lc, upstreamDB, "upstream")]);
64
+ }) : void 0;
65
+ const dbWarmup = Promise.allSettled([warmupConnections(lc, cvrDB, "cvr"), upstreamDB ? warmupConnections(lc, upstreamDB, "upstream") : promiseVoid]);
67
66
  const tmpDir = config.storageDBTmpDir ?? tmpdir();
68
67
  const operatorStorage = DatabaseStorage.create(lc, path.join(tmpDir, `sync-worker-${randomUUID()}`));
69
68
  const writeAuthzStorage = DatabaseStorage.create(lc, path.join(tmpDir, `mutagen-${randomUUID()}`));
@@ -79,8 +78,7 @@ function runWorker(parent, env, ...args) {
79
78
  const authSession = new AuthSessionImpl(logger.withContext("component", "auth-session"), id, validateLegacyJWT);
80
79
  return new ViewSyncerService(config, logger, shard, config.taskID, id, cvrDB, new PipelineDriver(logger, config.log, new Snapshotter(logger, replicaFile, shard), shard, operatorStorage.createClientGroupStorage(id), id, inspectorDelegate, () => isPriorityOpRunning() ? priorityOpRunningYieldThresholdMs : normalYieldThresholdMs, config.enableQueryPlanner, config), sub, drainCoordinator, config.log.slowHydrateThreshold, inspectorDelegate, customQueryTransformer, runPriorityOp, authSession);
81
80
  };
82
- const mutagenFactory = (id) => new MutagenService(lc.withContext("component", "mutagen").withContext("clientGroupID", id), shard, id, upstreamDB, config, writeAuthzStorage);
83
- const syncer = new Syncer(lc, config, viewSyncerFactory, mutagenFactory, config.push.url === void 0 && config.mutate.url === void 0 ? void 0 : (id) => new PusherService(config, {
81
+ const syncer = new Syncer(lc, config, viewSyncerFactory, upstreamDB ? (id) => new MutagenService(lc.withContext("component", "mutagen").withContext("clientGroupID", id), shard, id, upstreamDB, config, writeAuthzStorage) : void 0, config.push.url === void 0 && config.mutate.url === void 0 ? void 0 : (id) => new PusherService(config, {
84
82
  ...config.push,
85
83
  ...config.mutate,
86
84
  url: must(config.push.url ?? config.mutate.url, "No push or mutate URL configured")
@@ -1 +1 @@
1
- {"version":3,"file":"syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/syncer.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport {tmpdir} from 'node:os';\nimport path from 'node:path';\nimport {pid} from 'node:process';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {DatabaseStorage} from '../../../zqlite/src/database-storage.ts';\nimport {AuthSessionImpl, type ValidateLegacyJWT} from '../auth/auth.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {MutagenService} from '../services/mutagen/mutagen.ts';\nimport {PusherService} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport type {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport {PipelineDriver} from '../services/view-syncer/pipeline-driver.ts';\nimport {Snapshotter} from '../services/view-syncer/snapshotter.ts';\nimport {ViewSyncerService} from '../services/view-syncer/view-syncer.ts';\nimport {pgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardID} from '../types/shards.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {replicaFileModeSchema, replicaFileName} from '../workers/replicator.ts';\nimport {Syncer} from '../workers/syncer.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {InspectorDelegate} from './inspector-delegate.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {isPriorityOpRunning, runPriorityOp} from './priority-op.ts';\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\nfunction getCustomQueryConfig(\n config: Pick<NormalizedZeroConfig, 'query' | 'getQueries'>,\n) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n\n if (!queryConfig?.url) {\n return undefined;\n }\n\n return {\n url: queryConfig.url,\n forwardCookies: queryConfig.forwardCookies ?? false,\n };\n}\n\nexport default function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n const config = getNormalizedZeroConfig({env, argv: args.slice(1)});\n\n startOtelAuto(createLogContext(config, {worker: 'syncer'}, false));\n const lc = createLogContext(config, {worker: 'syncer'}, true);\n initEventSink(lc, config);\n\n assert(args.length > 0, `replicator mode not specified`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n\n const {cvr, upstream} = config;\n assert(cvr.maxConnsPerWorker, 'cvr.maxConnsPerWorker must be set');\n assert(upstream.maxConnsPerWorker, 'upstream.maxConnsPerWorker must be set');\n\n const replicaFile = replicaFileName(config.replica.file, fileMode);\n lc.debug?.(`running view-syncer on ${replicaFile}`);\n\n const cvrDB = pgClient(lc, cvr.db, {\n max: cvr.maxConnsPerWorker,\n connection: {['application_name']: `zero-sync-worker-${pid}-cvr`},\n });\n\n const upstreamDB = pgClient(lc, upstream.db, {\n max: upstream.maxConnsPerWorker,\n connection: {['application_name']: `zero-sync-worker-${pid}-upstream`},\n });\n\n const dbWarmup = Promise.allSettled([\n warmupConnections(lc, cvrDB, 'cvr'),\n warmupConnections(lc, upstreamDB, 'upstream'),\n ]);\n\n const tmpDir = config.storageDBTmpDir ?? tmpdir();\n const operatorStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `sync-worker-${randomUUID()}`),\n );\n const writeAuthzStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `mutagen-${randomUUID()}`),\n );\n\n const shard = getShardID(config);\n\n const viewSyncerFactory = (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n validateLegacyJWT: ValidateLegacyJWT | undefined,\n ) => {\n const logger = lc\n .withContext('component', 'view-syncer')\n .withContext('clientGroupID', id)\n .withContext('instance', randomID());\n lc.debug?.(\n `creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`,\n );\n\n // Create the custom query transformer if configured\n const customQueryConfig = getCustomQueryConfig(config);\n const customQueryTransformer =\n customQueryConfig &&\n new CustomQueryTransformer(logger, customQueryConfig, shard);\n\n const inspectorDelegate = new InspectorDelegate(customQueryTransformer);\n\n const priorityOpRunningYieldThresholdMs = Math.max(\n config.yieldThresholdMs / 4,\n 2,\n );\n const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);\n\n const authSession = new AuthSessionImpl(\n logger.withContext('component', 'auth-session'),\n id,\n validateLegacyJWT,\n );\n\n return new ViewSyncerService(\n config,\n logger,\n shard,\n config.taskID,\n id,\n cvrDB,\n new PipelineDriver(\n logger,\n config.log,\n new Snapshotter(logger, replicaFile, shard),\n shard,\n operatorStorage.createClientGroupStorage(id),\n id,\n inspectorDelegate,\n () =>\n isPriorityOpRunning()\n ? priorityOpRunningYieldThresholdMs\n : normalYieldThresholdMs,\n config.enableQueryPlanner,\n config,\n ),\n sub,\n drainCoordinator,\n config.log.slowHydrateThreshold,\n inspectorDelegate,\n customQueryTransformer,\n runPriorityOp,\n authSession,\n );\n };\n\n const mutagenFactory = (id: string) =>\n new MutagenService(\n lc.withContext('component', 'mutagen').withContext('clientGroupID', id),\n shard,\n id,\n upstreamDB,\n config,\n writeAuthzStorage,\n );\n\n const pusherFactory =\n config.push.url === undefined && config.mutate.url === undefined\n ? undefined\n : (id: string) =>\n new PusherService(\n config,\n {\n ...config.push,\n ...config.mutate,\n url: must(\n config.push.url ?? config.mutate.url,\n 'No push or mutate URL configured',\n ),\n },\n lc.withContext('clientGroupID', id),\n id,\n );\n\n const syncer = new Syncer(\n lc,\n config,\n viewSyncerFactory,\n mutagenFactory,\n pusherFactory,\n parent,\n );\n\n startAnonymousTelemetry(lc, config);\n\n void dbWarmup.then(() => parent.send(['ready', {ready: true}]));\n\n return runUntilKilled(lc, parent, syncer);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(() =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncer.js","names":[],"sources":["../../../../../zero-cache/src/server/syncer.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto';\nimport {tmpdir} from 'node:os';\nimport path from 'node:path';\nimport {pid} from 'node:process';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {DatabaseStorage} from '../../../zqlite/src/database-storage.ts';\nimport {AuthSessionImpl, type ValidateLegacyJWT} from '../auth/auth.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {getNormalizedZeroConfig} from '../config/zero-config.ts';\nimport {CustomQueryTransformer} from '../custom-queries/transform-query.ts';\nimport {warmupConnections} from '../db/warmup.ts';\nimport {initEventSink} from '../observability/events.ts';\nimport {exitAfter, runUntilKilled} from '../services/life-cycle.ts';\nimport {MutagenService} from '../services/mutagen/mutagen.ts';\nimport {PusherService} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport type {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport {PipelineDriver} from '../services/view-syncer/pipeline-driver.ts';\nimport {Snapshotter} from '../services/view-syncer/snapshotter.ts';\nimport {ViewSyncerService} from '../services/view-syncer/view-syncer.ts';\nimport {pgClient} from '../types/pg.ts';\nimport {\n parentWorker,\n singleProcessMode,\n type Worker,\n} from '../types/processes.ts';\nimport {getShardID} from '../types/shards.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {replicaFileModeSchema, replicaFileName} from '../workers/replicator.ts';\nimport {Syncer} from '../workers/syncer.ts';\nimport {startAnonymousTelemetry} from './anonymous-otel-start.ts';\nimport {InspectorDelegate} from './inspector-delegate.ts';\nimport {createLogContext} from './logging.ts';\nimport {startOtelAuto} from './otel-start.ts';\nimport {isPriorityOpRunning, runPriorityOp} from './priority-op.ts';\n\nfunction randomID() {\n return randInt(1, Number.MAX_SAFE_INTEGER).toString(36);\n}\n\nfunction getCustomQueryConfig(\n config: Pick<NormalizedZeroConfig, 'query' | 'getQueries'>,\n) {\n const queryConfig = config.query?.url ? config.query : config.getQueries;\n\n if (!queryConfig?.url) {\n return undefined;\n }\n\n return {\n url: queryConfig.url,\n forwardCookies: queryConfig.forwardCookies ?? false,\n };\n}\n\nexport default function runWorker(\n parent: Worker,\n env: NodeJS.ProcessEnv,\n ...args: string[]\n): Promise<void> {\n const config = getNormalizedZeroConfig({env, argv: args.slice(1)});\n\n startOtelAuto(createLogContext(config, {worker: 'syncer'}, false));\n const lc = createLogContext(config, {worker: 'syncer'}, true);\n initEventSink(lc, config);\n\n assert(args.length > 0, `replicator mode not specified`);\n const fileMode = v.parse(args[0], replicaFileModeSchema);\n\n const {cvr, upstream, enableCrudMutations} = config;\n\n const replicaFile = replicaFileName(config.replica.file, fileMode);\n lc.debug?.(`running view-syncer on ${replicaFile}`);\n\n const cvrDB = pgClient(lc, cvr.db, {\n max: must(cvr.maxConnsPerWorker, 'cvr.maxConnsPerWorker must be set'),\n connection: {['application_name']: `zero-sync-worker-${pid}-cvr`},\n });\n\n const upstreamDB = enableCrudMutations\n ? pgClient(lc, upstream.db, {\n max: must(\n upstream.maxConnsPerWorker,\n 'upstream.maxConnsPerWorker must be set',\n ),\n connection: {['application_name']: `zero-sync-worker-${pid}-upstream`},\n })\n : undefined;\n\n const dbWarmup = Promise.allSettled([\n warmupConnections(lc, cvrDB, 'cvr'),\n upstreamDB ? warmupConnections(lc, upstreamDB, 'upstream') : promiseVoid,\n ]);\n\n const tmpDir = config.storageDBTmpDir ?? tmpdir();\n const operatorStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `sync-worker-${randomUUID()}`),\n );\n const writeAuthzStorage = DatabaseStorage.create(\n lc,\n path.join(tmpDir, `mutagen-${randomUUID()}`),\n );\n\n const shard = getShardID(config);\n\n const viewSyncerFactory = (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n validateLegacyJWT: ValidateLegacyJWT | undefined,\n ) => {\n const logger = lc\n .withContext('component', 'view-syncer')\n .withContext('clientGroupID', id)\n .withContext('instance', randomID());\n lc.debug?.(\n `creating view syncer. Query Planner Enabled: ${config.enableQueryPlanner}`,\n );\n\n // Create the custom query transformer if configured\n const customQueryConfig = getCustomQueryConfig(config);\n const customQueryTransformer =\n customQueryConfig &&\n new CustomQueryTransformer(logger, customQueryConfig, shard);\n\n const inspectorDelegate = new InspectorDelegate(customQueryTransformer);\n\n const priorityOpRunningYieldThresholdMs = Math.max(\n config.yieldThresholdMs / 4,\n 2,\n );\n const normalYieldThresholdMs = Math.max(config.yieldThresholdMs, 2);\n\n const authSession = new AuthSessionImpl(\n logger.withContext('component', 'auth-session'),\n id,\n validateLegacyJWT,\n );\n\n return new ViewSyncerService(\n config,\n logger,\n shard,\n config.taskID,\n id,\n cvrDB,\n new PipelineDriver(\n logger,\n config.log,\n new Snapshotter(logger, replicaFile, shard),\n shard,\n operatorStorage.createClientGroupStorage(id),\n id,\n inspectorDelegate,\n () =>\n isPriorityOpRunning()\n ? priorityOpRunningYieldThresholdMs\n : normalYieldThresholdMs,\n config.enableQueryPlanner,\n config,\n ),\n sub,\n drainCoordinator,\n config.log.slowHydrateThreshold,\n inspectorDelegate,\n customQueryTransformer,\n runPriorityOp,\n authSession,\n );\n };\n\n const mutagenFactory = upstreamDB\n ? (id: string) =>\n new MutagenService(\n lc\n .withContext('component', 'mutagen')\n .withContext('clientGroupID', id),\n shard,\n id,\n upstreamDB,\n config,\n writeAuthzStorage,\n )\n : undefined;\n\n const pusherFactory =\n config.push.url === undefined && config.mutate.url === undefined\n ? undefined\n : (id: string) =>\n new PusherService(\n config,\n {\n ...config.push,\n ...config.mutate,\n url: must(\n config.push.url ?? config.mutate.url,\n 'No push or mutate URL configured',\n ),\n },\n lc.withContext('clientGroupID', id),\n id,\n );\n\n const syncer = new Syncer(\n lc,\n config,\n viewSyncerFactory,\n mutagenFactory,\n pusherFactory,\n parent,\n );\n\n startAnonymousTelemetry(lc, config);\n\n void dbWarmup.then(() => parent.send(['ready', {ready: true}]));\n\n return runUntilKilled(lc, parent, syncer);\n}\n\n// fork()\nif (!singleProcessMode()) {\n void exitAfter(() =>\n runWorker(must(parentWorker), process.env, ...process.argv.slice(2)),\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"worker-urls.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/worker-urls.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,mBAAmB,KAAkC,CAAC;AACnE,eAAO,MAAM,QAAQ,KAAuB,CAAC;AAC7C,eAAO,MAAM,WAAW,KAA0B,CAAC;AACnD,eAAO,MAAM,UAAU,KAAyB,CAAC;AACjD,eAAO,MAAM,cAAc,KAA6B,CAAC;AACzD,eAAO,MAAM,UAAU,KAAyB,CAAC;AACjD,eAAO,MAAM,gBAAgB,KAA+B,CAAC"}
1
+ {"version":3,"file":"worker-urls.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/server/worker-urls.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,mBAAmB,KAAkC,CAAC;AACnE,eAAO,MAAM,QAAQ,KAAuB,CAAC;AAC7C,eAAO,MAAM,WAAW,KAA0B,CAAC;AACnD,eAAO,MAAM,UAAU,KAAyB,CAAC;AACjD,eAAO,MAAM,cAAc,KAA6B,CAAC;AACzD,eAAO,MAAM,UAAU,KAAyB,CAAC;AACjD,eAAO,MAAM,gBAAgB,KAA+B,CAAC"}
@@ -1,7 +1,8 @@
1
1
  //#region ../zero-cache/src/server/worker-urls.ts
2
+ var tsRe = /\.ts$/;
2
3
  function resolve(path) {
3
4
  const { url } = import.meta;
4
- if (url.endsWith(".js")) path = path.replace(/\.ts$/, ".js");
5
+ if (url.endsWith(".js")) path = path.replace(tsRe, ".js");
5
6
  return new URL(path, url);
6
7
  }
7
8
  var CHANGE_STREAMER_URL = resolve("./change-streamer.ts");
@@ -1 +1 @@
1
- {"version":3,"file":"worker-urls.js","names":[],"sources":["../../../../../zero-cache/src/server/worker-urls.ts"],"sourcesContent":["// This module provides URLs for worker files.\n\nfunction resolve(path: string): URL {\n const {url} = import.meta;\n if (url.endsWith('.js')) {\n // When compiled, change .ts to .js\n path = path.replace(/\\.ts$/, '.js');\n }\n return new URL(path, url);\n}\n\n// These URLs are part of the build process. See ../../zero/tool/build.ts\n// All these urls must be relative to this file and be located in the same directory.\n\nexport const CHANGE_STREAMER_URL = resolve('./change-streamer.ts');\nexport const MAIN_URL = resolve('./main.ts');\nexport const MUTATOR_URL = resolve('./mutator.ts');\nexport const REAPER_URL = resolve('./reaper.ts');\nexport const REPLICATOR_URL = resolve('./replicator.ts');\nexport const SYNCER_URL = resolve('./syncer.ts');\nexport const WRITE_WORKER_URL = resolve('./write-worker.ts');\n"],"mappings":";AAEA,SAAS,QAAQ,MAAmB;CAClC,MAAM,EAAC,QAAO,OAAO;AACrB,KAAI,IAAI,SAAS,MAAM,CAErB,QAAO,KAAK,QAAQ,SAAS,MAAM;AAErC,QAAO,IAAI,IAAI,MAAM,IAAI;;AAM3B,IAAa,sBAAsB,QAAQ,uBAAuB;AAClE,IAAa,WAAW,QAAQ,YAAY;AACjB,QAAQ,eAAe;AAClD,IAAa,aAAa,QAAQ,cAAc;AAChD,IAAa,iBAAiB,QAAQ,kBAAkB;AACxD,IAAa,aAAa,QAAQ,cAAc;AAChD,IAAa,mBAAmB,QAAQ,oBAAoB"}
1
+ {"version":3,"file":"worker-urls.js","names":[],"sources":["../../../../../zero-cache/src/server/worker-urls.ts"],"sourcesContent":["// This module provides URLs for worker files.\n\nconst tsRe = /\\.ts$/;\n\nfunction resolve(path: string): URL {\n const {url} = import.meta;\n if (url.endsWith('.js')) {\n // When compiled, change .ts to .js\n path = path.replace(tsRe, '.js');\n }\n return new URL(path, url);\n}\n\n// These URLs are part of the build process. See ../../zero/tool/build.ts\n// All these urls must be relative to this file and be located in the same directory.\n\nexport const CHANGE_STREAMER_URL = resolve('./change-streamer.ts');\nexport const MAIN_URL = resolve('./main.ts');\nexport const MUTATOR_URL = resolve('./mutator.ts');\nexport const REAPER_URL = resolve('./reaper.ts');\nexport const REPLICATOR_URL = resolve('./replicator.ts');\nexport const SYNCER_URL = resolve('./syncer.ts');\nexport const WRITE_WORKER_URL = resolve('./write-worker.ts');\n"],"mappings":";AAEA,IAAM,OAAO;AAEb,SAAS,QAAQ,MAAmB;CAClC,MAAM,EAAC,QAAO,OAAO;AACrB,KAAI,IAAI,SAAS,MAAM,CAErB,QAAO,KAAK,QAAQ,MAAM,MAAM;AAElC,QAAO,IAAI,IAAI,MAAM,IAAI;;AAM3B,IAAa,sBAAsB,QAAQ,uBAAuB;AAClE,IAAa,WAAW,QAAQ,YAAY;AACjB,QAAQ,eAAe;AAClD,IAAa,aAAa,QAAQ,cAAc;AAChD,IAAa,iBAAiB,QAAQ,kBAAkB;AACxD,IAAa,aAAa,QAAQ,cAAc;AAChD,IAAa,mBAAmB,QAAQ,oBAAoB"}
@@ -22,5 +22,9 @@ export interface ChangeSource {
22
22
  * with a corresponding sink for upstream acknowledgements.
23
23
  */
24
24
  startStream(afterWatermark: string, backfillRequests?: BackfillRequest[]): Promise<ChangeStream>;
25
+ /**
26
+ * Releases connections and resources held by this change source.
27
+ */
28
+ stop(): Promise<void>;
25
29
  }
26
30
  //# sourceMappingURL=change-source.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-source/change-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAE,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAErC;;;;OAIG;IACH,IAAI,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;CAClC,CAAC,CAAC,+EAA+E;AAElF,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,gBAAgB,IAAI,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAEpE;;;OAGG;IACH,WAAW,CACT,cAAc,EAAE,MAAM,EACtB,gBAAgB,CAAC,EAAE,eAAe,EAAE,GACnC,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1B"}
1
+ {"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/change-source/change-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAE,MAAM,EAAC,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAErC;;;;OAIG;IACH,IAAI,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;CAClC,CAAC,CAAC,+EAA+E;AAElF,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,gBAAgB,IAAI,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAEpE;;;OAGG;IACH,WAAW,CACT,cAAc,EAAE,MAAM,EACtB,gBAAgB,CAAC,EAAE,eAAe,EAAE,GACnC,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzB;;OAEG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAEL,KAAK,UAAU,EAChB,MAAM,0CAA0C,CAAC;AAGlD,OAAO,EAAC,QAAQ,EAAC,MAAM,iCAAiC,CAAC;AAGzD,OAAO,KAAK,EAAC,WAAW,EAAU,MAAM,0BAA0B,CAAC;AAQnE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAC,YAAY,EAAe,MAAM,qBAAqB,CAAC;AAQpE,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC;AAEvC;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAC,CAAC,CA+B7E;AAuED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,aAAa,iBAqFvB"}
1
+ {"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAEL,KAAK,UAAU,EAChB,MAAM,0CAA0C,CAAC;AAGlD,OAAO,EAAC,QAAQ,EAAC,MAAM,iCAAiC,CAAC;AAGzD,OAAO,KAAK,EAAC,WAAW,EAAU,MAAM,0BAA0B,CAAC;AAQnE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAC,YAAY,EAAe,MAAM,qBAAqB,CAAC;AAQpE,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC;AAEvC;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAC,CAAC,CA+B7E;AA2ED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,EAClB,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,aAAa,iBAqFvB"}
@@ -12,7 +12,7 @@ import { initReplica } from "../common/replica-schema.js";
12
12
  import { stream } from "../../../types/streams.js";
13
13
  import { ChangeProcessor } from "../../replicator/change-processor.js";
14
14
  import { ReplicationStatusPublisher } from "../../replicator/replication-status.js";
15
- import { WebSocket } from "ws";
15
+ import { WebSocket as WebSocket$1 } from "ws";
16
16
  //#region ../zero-cache/src/services/change-source/custom/change-source.ts
17
17
  /**
18
18
  * Initializes a Custom change source before streaming changes from the
@@ -24,7 +24,7 @@ async function initializeCustomChangeSource(lc, upstreamURI, shard, replicaDbFil
24
24
  const subscriptionState = getSubscriptionState(new StatementRunner(replica));
25
25
  replica.close();
26
26
  if (shard.publications.length) {
27
- const requested = [...shard.publications].sort();
27
+ const requested = shard.publications.toSorted();
28
28
  const replicated = subscriptionState.publications.sort();
29
29
  if (!deepEqual(requested, replicated)) throw new Error(`Invalid ShardConfig. Requested publications [${requested}] do not match synced publications: [${replicated}]`);
30
30
  }
@@ -50,6 +50,9 @@ var CustomChangeSource = class {
50
50
  startLagReporter() {
51
51
  return null;
52
52
  }
53
+ stop() {
54
+ return Promise.resolve();
55
+ }
53
56
  startStream(clientWatermark, backfillRequests = []) {
54
57
  if (backfillRequests?.length) throw new Error("backfill is yet not supported for custom change sources");
55
58
  return Promise.resolve(this.#startStream(clientWatermark));
@@ -66,7 +69,7 @@ var CustomChangeSource = class {
66
69
  url.searchParams.set("lastWatermark", clientWatermark);
67
70
  url.searchParams.set("replicaVersion", replicaVersion);
68
71
  }
69
- const ws = new WebSocket(url);
72
+ const ws = new WebSocket$1(url);
70
73
  const { instream, outstream } = stream(this.#lc, ws, changeStreamMessageSchema, { coalesce: (curr) => curr });
71
74
  return {
72
75
  changes: instream,
@@ -105,7 +108,7 @@ async function initialSync(lc, shard, tx, upstreamURI, context) {
105
108
  const { commitWatermark } = change[2];
106
109
  lc.info?.(`initial sync of shard ${id} at replicaVersion ${commitWatermark}`);
107
110
  statusPublisher.publish(lc, "Initializing", `Copying upstream tables at version ${commitWatermark}`, 5e3);
108
- initReplicationState(tx, [...publications].sort(), commitWatermark, context, false);
111
+ initReplicationState(tx, publications.toSorted(), commitWatermark, context, false);
109
112
  processor.processMessage(lc, change);
110
113
  break;
111
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"change-source.js","names":["#lc","#upstreamUri","#shard","#replicationConfig","#startStream"],"sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {WebSocket} from 'ws';\nimport {assert, unreachable} from '../../../../../shared/src/asserts.ts';\nimport {\n stringify,\n type JSONObject,\n} from '../../../../../shared/src/bigint-json.ts';\nimport {deepEqual} from '../../../../../shared/src/json.ts';\nimport type {SchemaValue} from '../../../../../zero-schema/src/table-schema.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {computeZqlSpecs} from '../../../db/lite-tables.ts';\nimport {StatementRunner} from '../../../db/statements.ts';\nimport type {ShardConfig, ShardID} from '../../../types/shards.ts';\nimport {stream} from '../../../types/streams.ts';\nimport {\n AutoResetSignal,\n type ReplicationConfig,\n} from '../../change-streamer/schema/tables.ts';\nimport {ChangeProcessor} from '../../replicator/change-processor.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {\n createReplicationStateTables,\n getSubscriptionState,\n initReplicationState,\n type SubscriptionState,\n} from '../../replicator/schema/replication-state.ts';\nimport type {ChangeSource, ChangeStream} from '../change-source.ts';\nimport {initReplica} from '../common/replica-schema.ts';\nimport {changeStreamMessageSchema} from '../protocol/current/downstream.ts';\nimport {\n type BackfillRequest,\n type ChangeSourceUpstream,\n} from '../protocol/current/upstream.ts';\n\n/** Server context to store with the initial sync metadata for debugging. */\nexport type ServerContext = JSONObject;\n\n/**\n * Initializes a Custom change source before streaming changes from the\n * corresponding logical replication stream.\n */\nexport async function initializeCustomChangeSource(\n lc: LogContext,\n upstreamURI: string,\n shard: ShardConfig,\n replicaDbFile: string,\n context: ServerContext,\n): Promise<{subscriptionState: SubscriptionState; changeSource: ChangeSource}> {\n await initReplica(\n lc,\n `replica-${shard.appID}-${shard.shardNum}`,\n replicaDbFile,\n (log, tx) => initialSync(log, shard, tx, upstreamURI, context),\n );\n\n const replica = new Database(lc, replicaDbFile);\n const subscriptionState = getSubscriptionState(new StatementRunner(replica));\n replica.close();\n\n if (shard.publications.length) {\n // Verify that the publications match what has been synced.\n const requested = [...shard.publications].sort();\n const replicated = subscriptionState.publications.sort();\n if (!deepEqual(requested, replicated)) {\n throw new Error(\n `Invalid ShardConfig. Requested publications [${requested}] do not match synced publications: [${replicated}]`,\n );\n }\n }\n\n const changeSource = new CustomChangeSource(\n lc,\n upstreamURI,\n shard,\n subscriptionState,\n );\n\n return {subscriptionState, changeSource};\n}\n\nclass CustomChangeSource implements ChangeSource {\n readonly #lc: LogContext;\n readonly #upstreamUri: string;\n readonly #shard: ShardID;\n readonly #replicationConfig: ReplicationConfig;\n\n constructor(\n lc: LogContext,\n upstreamUri: string,\n shard: ShardID,\n replicationConfig: ReplicationConfig,\n ) {\n this.#lc = lc.withContext('component', 'change-source');\n this.#upstreamUri = upstreamUri;\n this.#shard = shard;\n this.#replicationConfig = replicationConfig;\n }\n\n initialSync(): ChangeStream {\n return this.#startStream();\n }\n\n startLagReporter() {\n return null; // Not supported for custom sources\n }\n\n startStream(\n clientWatermark: string,\n backfillRequests: BackfillRequest[] = [],\n ): Promise<ChangeStream> {\n if (backfillRequests?.length) {\n throw new Error(\n 'backfill is yet not supported for custom change sources',\n );\n }\n return Promise.resolve(this.#startStream(clientWatermark));\n }\n\n #startStream(clientWatermark?: string): ChangeStream {\n const {publications, replicaVersion} = this.#replicationConfig;\n const {appID, shardNum} = this.#shard;\n const url = new URL(this.#upstreamUri);\n url.searchParams.set('appID', appID);\n url.searchParams.set('shardNum', String(shardNum));\n for (const pub of publications) {\n url.searchParams.append('publications', pub);\n }\n if (clientWatermark) {\n assert(\n replicaVersion.length,\n 'replicaVersion is required when clientWatermark is set',\n );\n url.searchParams.set('lastWatermark', clientWatermark);\n url.searchParams.set('replicaVersion', replicaVersion);\n }\n\n const ws = new WebSocket(url);\n const {instream, outstream} = stream(\n this.#lc,\n ws,\n changeStreamMessageSchema,\n // Upstream acks coalesce. If upstream exhibits back-pressure,\n // only the last ACK is kept / buffered.\n {coalesce: (curr: ChangeSourceUpstream) => curr},\n );\n return {changes: instream, acks: outstream};\n }\n}\n\n/**\n * Initial sync for a custom change source makes a request to the\n * change source endpoint with no `replicaVersion` or `lastWatermark`.\n * The initial transaction returned by the endpoint is treated as\n * the initial sync, and the commit watermark of that transaction\n * becomes the `replicaVersion` of the initialized replica.\n *\n * Note that this is equivalent to how the LSN of the Postgres WAL\n * at initial sync time is the `replicaVersion` (and starting\n * version for all initially-synced rows).\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n context: ServerContext,\n) {\n const {appID: id, publications} = shard;\n const changeSource = new CustomChangeSource(lc, upstreamURI, shard, {\n replicaVersion: '', // ignored for initialSync()\n publications,\n });\n const {changes} = changeSource.initialSync();\n\n createReplicationStateTables(tx);\n const processor = new ChangeProcessor(\n new StatementRunner(tx),\n 'initial-sync',\n (_, err) => {\n throw err;\n },\n );\n\n const statusPublisher = ReplicationStatusPublisher.forRunningTransaction(tx);\n try {\n let num = 0;\n for await (const change of changes) {\n const [tag] = change;\n switch (tag) {\n case 'begin': {\n const {commitWatermark} = change[2];\n lc.info?.(\n `initial sync of shard ${id} at replicaVersion ${commitWatermark}`,\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying upstream tables at version ${commitWatermark}`,\n 5000,\n );\n initReplicationState(\n tx,\n [...publications].sort(),\n commitWatermark,\n context,\n false,\n );\n processor.processMessage(lc, change);\n break;\n }\n case 'data':\n processor.processMessage(lc, change);\n if (++num % 1000 === 0) {\n lc.debug?.(`processed ${num} changes`);\n }\n break;\n case 'commit':\n processor.processMessage(lc, change);\n validateInitiallySyncedData(lc, tx, shard);\n lc.info?.(`finished initial-sync of ${num} changes`);\n return;\n\n case 'status':\n break; // Ignored\n // @ts-expect-error: falls through if the tag is not 'reset-required\n case 'control': {\n const {tag, message} = change[1];\n if (tag === 'reset-required') {\n throw new AutoResetSignal(\n message ?? 'auto-reset signaled by change source',\n );\n }\n }\n // falls through\n case 'rollback':\n throw new Error(\n `unexpected message during initial-sync: ${stringify(change)}`,\n );\n default:\n unreachable(change);\n }\n }\n throw new Error(\n `change source ${upstreamURI} closed before initial-sync completed`,\n );\n } catch (e) {\n await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n }\n}\n\n// Verify that the upstream tables expected by the sync logic\n// have been properly initialized.\nfunction getRequiredTables({\n appID,\n shardNum,\n}: ShardID): Record<string, Record<string, SchemaValue>> {\n return {\n [`${appID}_${shardNum}.clients`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n lastMutationID: {type: 'number'},\n userID: {type: 'string'},\n },\n [`${appID}_${shardNum}.mutations`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n mutationID: {type: 'number'},\n mutation: {type: 'json'},\n },\n [`${appID}.permissions`]: {\n permissions: {type: 'json'},\n hash: {type: 'string'},\n },\n };\n}\n\nfunction validateInitiallySyncedData(\n lc: LogContext,\n db: Database,\n shard: ShardID,\n) {\n const tables = computeZqlSpecs(lc, db, {includeBackfillingColumns: true});\n const required = getRequiredTables(shard);\n for (const [name, columns] of Object.entries(required)) {\n const table = tables.get(name)?.zqlSpec;\n if (!table) {\n throw new Error(\n `Upstream is missing the \"${name}\" table. (Found ${[\n ...tables.keys(),\n ]})` +\n `Please ensure that each table has a unique index over one ` +\n `or more non-null columns.`,\n );\n }\n for (const [col, {type}] of Object.entries(columns)) {\n const found = table[col];\n if (!found) {\n throw new Error(\n `Upstream \"${table}\" table is missing the \"${col}\" column`,\n );\n }\n if (found.type !== type) {\n throw new Error(\n `Upstream \"${table}.${col}\" column is a ${found.type} type but must be a ${type} type.`,\n );\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"change-source.js","names":["#lc","#upstreamUri","#shard","#replicationConfig","#startStream"],"sources":["../../../../../../../zero-cache/src/services/change-source/custom/change-source.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {WebSocket} from 'ws';\nimport {assert, unreachable} from '../../../../../shared/src/asserts.ts';\nimport {\n stringify,\n type JSONObject,\n} from '../../../../../shared/src/bigint-json.ts';\nimport {deepEqual} from '../../../../../shared/src/json.ts';\nimport type {SchemaValue} from '../../../../../zero-schema/src/table-schema.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {computeZqlSpecs} from '../../../db/lite-tables.ts';\nimport {StatementRunner} from '../../../db/statements.ts';\nimport type {ShardConfig, ShardID} from '../../../types/shards.ts';\nimport {stream} from '../../../types/streams.ts';\nimport {\n AutoResetSignal,\n type ReplicationConfig,\n} from '../../change-streamer/schema/tables.ts';\nimport {ChangeProcessor} from '../../replicator/change-processor.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {\n createReplicationStateTables,\n getSubscriptionState,\n initReplicationState,\n type SubscriptionState,\n} from '../../replicator/schema/replication-state.ts';\nimport type {ChangeSource, ChangeStream} from '../change-source.ts';\nimport {initReplica} from '../common/replica-schema.ts';\nimport {changeStreamMessageSchema} from '../protocol/current/downstream.ts';\nimport {\n type BackfillRequest,\n type ChangeSourceUpstream,\n} from '../protocol/current/upstream.ts';\n\n/** Server context to store with the initial sync metadata for debugging. */\nexport type ServerContext = JSONObject;\n\n/**\n * Initializes a Custom change source before streaming changes from the\n * corresponding logical replication stream.\n */\nexport async function initializeCustomChangeSource(\n lc: LogContext,\n upstreamURI: string,\n shard: ShardConfig,\n replicaDbFile: string,\n context: ServerContext,\n): Promise<{subscriptionState: SubscriptionState; changeSource: ChangeSource}> {\n await initReplica(\n lc,\n `replica-${shard.appID}-${shard.shardNum}`,\n replicaDbFile,\n (log, tx) => initialSync(log, shard, tx, upstreamURI, context),\n );\n\n const replica = new Database(lc, replicaDbFile);\n const subscriptionState = getSubscriptionState(new StatementRunner(replica));\n replica.close();\n\n if (shard.publications.length) {\n // Verify that the publications match what has been synced.\n const requested = shard.publications.toSorted();\n const replicated = subscriptionState.publications.sort();\n if (!deepEqual(requested, replicated)) {\n throw new Error(\n `Invalid ShardConfig. Requested publications [${requested}] do not match synced publications: [${replicated}]`,\n );\n }\n }\n\n const changeSource = new CustomChangeSource(\n lc,\n upstreamURI,\n shard,\n subscriptionState,\n );\n\n return {subscriptionState, changeSource};\n}\n\nclass CustomChangeSource implements ChangeSource {\n readonly #lc: LogContext;\n readonly #upstreamUri: string;\n readonly #shard: ShardID;\n readonly #replicationConfig: ReplicationConfig;\n\n constructor(\n lc: LogContext,\n upstreamUri: string,\n shard: ShardID,\n replicationConfig: ReplicationConfig,\n ) {\n this.#lc = lc.withContext('component', 'change-source');\n this.#upstreamUri = upstreamUri;\n this.#shard = shard;\n this.#replicationConfig = replicationConfig;\n }\n\n initialSync(): ChangeStream {\n return this.#startStream();\n }\n\n startLagReporter() {\n return null; // Not supported for custom sources\n }\n\n stop(): Promise<void> {\n return Promise.resolve();\n }\n\n startStream(\n clientWatermark: string,\n backfillRequests: BackfillRequest[] = [],\n ): Promise<ChangeStream> {\n if (backfillRequests?.length) {\n throw new Error(\n 'backfill is yet not supported for custom change sources',\n );\n }\n return Promise.resolve(this.#startStream(clientWatermark));\n }\n\n #startStream(clientWatermark?: string): ChangeStream {\n const {publications, replicaVersion} = this.#replicationConfig;\n const {appID, shardNum} = this.#shard;\n const url = new URL(this.#upstreamUri);\n url.searchParams.set('appID', appID);\n url.searchParams.set('shardNum', String(shardNum));\n for (const pub of publications) {\n url.searchParams.append('publications', pub);\n }\n if (clientWatermark) {\n assert(\n replicaVersion.length,\n 'replicaVersion is required when clientWatermark is set',\n );\n url.searchParams.set('lastWatermark', clientWatermark);\n url.searchParams.set('replicaVersion', replicaVersion);\n }\n\n const ws = new WebSocket(url);\n const {instream, outstream} = stream(\n this.#lc,\n ws,\n changeStreamMessageSchema,\n // Upstream acks coalesce. If upstream exhibits back-pressure,\n // only the last ACK is kept / buffered.\n {coalesce: (curr: ChangeSourceUpstream) => curr},\n );\n return {changes: instream, acks: outstream};\n }\n}\n\n/**\n * Initial sync for a custom change source makes a request to the\n * change source endpoint with no `replicaVersion` or `lastWatermark`.\n * The initial transaction returned by the endpoint is treated as\n * the initial sync, and the commit watermark of that transaction\n * becomes the `replicaVersion` of the initialized replica.\n *\n * Note that this is equivalent to how the LSN of the Postgres WAL\n * at initial sync time is the `replicaVersion` (and starting\n * version for all initially-synced rows).\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n context: ServerContext,\n) {\n const {appID: id, publications} = shard;\n const changeSource = new CustomChangeSource(lc, upstreamURI, shard, {\n replicaVersion: '', // ignored for initialSync()\n publications,\n });\n const {changes} = changeSource.initialSync();\n\n createReplicationStateTables(tx);\n const processor = new ChangeProcessor(\n new StatementRunner(tx),\n 'initial-sync',\n (_, err) => {\n throw err;\n },\n );\n\n const statusPublisher = ReplicationStatusPublisher.forRunningTransaction(tx);\n try {\n let num = 0;\n for await (const change of changes) {\n const [tag] = change;\n switch (tag) {\n case 'begin': {\n const {commitWatermark} = change[2];\n lc.info?.(\n `initial sync of shard ${id} at replicaVersion ${commitWatermark}`,\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying upstream tables at version ${commitWatermark}`,\n 5000,\n );\n initReplicationState(\n tx,\n publications.toSorted(),\n commitWatermark,\n context,\n false,\n );\n processor.processMessage(lc, change);\n break;\n }\n case 'data':\n processor.processMessage(lc, change);\n if (++num % 1000 === 0) {\n lc.debug?.(`processed ${num} changes`);\n }\n break;\n case 'commit':\n processor.processMessage(lc, change);\n validateInitiallySyncedData(lc, tx, shard);\n lc.info?.(`finished initial-sync of ${num} changes`);\n return;\n\n case 'status':\n break; // Ignored\n // @ts-expect-error: falls through if the tag is not 'reset-required\n case 'control': {\n const {tag, message} = change[1];\n if (tag === 'reset-required') {\n throw new AutoResetSignal(\n message ?? 'auto-reset signaled by change source',\n );\n }\n }\n // falls through\n case 'rollback':\n throw new Error(\n `unexpected message during initial-sync: ${stringify(change)}`,\n );\n default:\n unreachable(change);\n }\n }\n throw new Error(\n `change source ${upstreamURI} closed before initial-sync completed`,\n );\n } catch (e) {\n await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n }\n}\n\n// Verify that the upstream tables expected by the sync logic\n// have been properly initialized.\nfunction getRequiredTables({\n appID,\n shardNum,\n}: ShardID): Record<string, Record<string, SchemaValue>> {\n return {\n [`${appID}_${shardNum}.clients`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n lastMutationID: {type: 'number'},\n userID: {type: 'string'},\n },\n [`${appID}_${shardNum}.mutations`]: {\n clientGroupID: {type: 'string'},\n clientID: {type: 'string'},\n mutationID: {type: 'number'},\n mutation: {type: 'json'},\n },\n [`${appID}.permissions`]: {\n permissions: {type: 'json'},\n hash: {type: 'string'},\n },\n };\n}\n\nfunction validateInitiallySyncedData(\n lc: LogContext,\n db: Database,\n shard: ShardID,\n) {\n const tables = computeZqlSpecs(lc, db, {includeBackfillingColumns: true});\n const required = getRequiredTables(shard);\n for (const [name, columns] of Object.entries(required)) {\n const table = tables.get(name)?.zqlSpec;\n if (!table) {\n throw new Error(\n `Upstream is missing the \"${name}\" table. (Found ${[\n ...tables.keys(),\n ]})` +\n `Please ensure that each table has a unique index over one ` +\n `or more non-null columns.`,\n );\n }\n for (const [col, {type}] of Object.entries(columns)) {\n const found = table[col];\n if (!found) {\n throw new Error(\n `Upstream \"${table}\" table is missing the \"${col}\" column`,\n );\n }\n if (found.type !== type) {\n throw new Error(\n `Upstream \"${table}.${col}\" column is a ${found.type} type but must be a ${type} type.`,\n );\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/change-source.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAejD,OAAO,KAAK,CAAC,MAAM,qCAAqC,CAAC;AAOzD,OAAO,KAAK,EAGV,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,2BAA2B,CAAC;AAEpD,OAAO,EAEL,KAAK,iBAAiB,EAEvB,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAC,YAAY,EAAe,MAAM,qBAAqB,CAAC;AAEpE,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,wCAAwC,CAAC;AAchD,OAAO,KAAK,EAEV,mBAAmB,EAEpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAGV,eAAe,IAAI,gBAAgB,EACpC,MAAM,yCAAyC,CAAC;AA6BjD;;;;GAIG;AACH,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,kBAAkB,EAC/B,OAAO,EAAE,aAAa,EACtB,mBAAmB,SAAI,GACtB,OAAO,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAC,CAAC,CAsC7E;AA+bD,qBAAa,KAAM,YAAW,QAAQ;;gBAIxB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;IAI9B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAgC3C,GAAG,CAAC,SAAS,EAAE,WAAW;CAoB3B;AAED,QAAA,MAAM,eAAe;;;;aAInB,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AA8vBxD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,gBAAgB,WAwB3E"}
1
+ {"version":3,"file":"change-source.d.ts","sourceRoot":"","sources":["../../../../../../../zero-cache/src/services/change-source/pg/change-source.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAejD,OAAO,KAAK,CAAC,MAAM,qCAAqC,CAAC;AAOzD,OAAO,KAAK,EAGV,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,gCAAgC,CAAC;AAEhE,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,2BAA2B,CAAC;AAEpD,OAAO,EAEL,KAAK,iBAAiB,EAEvB,MAAM,8CAA8C,CAAC;AACtD,OAAO,KAAK,EAAC,YAAY,EAAe,MAAM,qBAAqB,CAAC;AAEpE,OAAO,EAEL,KAAK,QAAQ,EACd,MAAM,wCAAwC,CAAC;AAchD,OAAO,KAAK,EAEV,mBAAmB,EAEpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAGV,eAAe,IAAI,gBAAgB,EACpC,MAAM,yCAAyC,CAAC;AA6BjD;;;;GAIG;AACH,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,UAAU,EACd,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,kBAAkB,EAC/B,OAAO,EAAE,aAAa,EACtB,mBAAmB,SAAI,GACtB,OAAO,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,YAAY,CAAA;CAAC,CAAC,CAsC7E;AAocD,qBAAa,KAAM,YAAW,QAAQ;;gBAIxB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;IAI9B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI;IAgC3C,GAAG,CAAC,SAAS,EAAE,WAAW;CAoB3B;AAED,QAAA,MAAM,eAAe;;;;aAInB,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAmwBxD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,gBAAgB,WAwB3E"}
@@ -57,7 +57,7 @@ async function checkAndUpdateUpstream(lc, sql, shard, { replicaVersion, publicat
57
57
  await updateShardSchema(lc, sql, shard, replicaVersion);
58
58
  const upstreamReplica = await getReplicaAtVersion(lc, sql, shard, replicaVersion, initialSyncContext);
59
59
  if (!upstreamReplica) throw new AutoResetSignal(`No replication slot for replica at version ${replicaVersion}`);
60
- const requested = [...shard.publications].sort();
60
+ const requested = shard.publications.toSorted();
61
61
  const replicated = upstreamReplica.publications.filter((p) => !p.startsWith(internalPublicationPrefix(shard))).sort();
62
62
  if (!deepEqual(requested, replicated)) {
63
63
  lc.warn?.(`Dropping shard to change publications to: [${requested}]`);
@@ -103,6 +103,10 @@ var PostgresChangeSource = class {
103
103
  this.#context = context;
104
104
  this.#lagReporter = lagReportIntervalMs > 0 ? new LagReporter(lc.withContext("component", "lag-reporter"), shard, this.#db, lagReportIntervalMs) : null;
105
105
  }
106
+ async stop() {
107
+ this.#lagReporter?.stop();
108
+ await this.#db.end();
109
+ }
106
110
  async startLagReporter() {
107
111
  if (this.#lagReporter) try {
108
112
  return await this.#lagReporter.initiateLagReport(true);
@@ -414,6 +418,10 @@ var LagReporter = class LagReporter {
414
418
  return this.#processLagReport(this.#expectingLagReport, majorVersionToString(lsn));
415
419
  }
416
420
  }
421
+ stop() {
422
+ clearTimeout(this.#timer);
423
+ this.#timer = void 0;
424
+ }
417
425
  #scheduleNextReport(delayMs) {
418
426
  this.#expectingLagReport = null;
419
427
  clearTimeout(this.#timer);