@rocicorp/zero 0.25.0-canary.18 → 0.25.0-canary.21

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 (228) hide show
  1. package/out/replicache/src/persist/idb-databases-store.d.ts +1 -0
  2. package/out/replicache/src/persist/idb-databases-store.d.ts.map +1 -1
  3. package/out/replicache/src/persist/idb-databases-store.js +13 -2
  4. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  5. package/out/zero/package.json.js +1 -1
  6. package/out/zero/src/adapters/drizzle.d.ts +1 -1
  7. package/out/zero/src/adapters/drizzle.d.ts.map +1 -1
  8. package/out/zero/src/adapters/drizzle.js +4 -1
  9. package/out/zero/src/bindings.d.ts +2 -0
  10. package/out/zero/src/bindings.d.ts.map +1 -0
  11. package/out/zero/src/bindings.js +27 -0
  12. package/out/zero/src/bindings.js.map +1 -0
  13. package/out/zero/src/react.js +2 -4
  14. package/out/zero/src/react.js.map +1 -1
  15. package/out/zero/src/solid.js +2 -2
  16. package/out/zero/src/zero.js +3 -5
  17. package/out/zero-cache/src/auth/read-authorizer.d.ts +1 -1
  18. package/out/zero-cache/src/auth/read-authorizer.d.ts.map +1 -1
  19. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  20. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  21. package/out/zero-cache/src/auth/write-authorizer.js +5 -4
  22. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  23. package/out/zero-cache/src/config/zero-config.d.ts +24 -0
  24. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  25. package/out/zero-cache/src/config/zero-config.js +23 -4
  26. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  27. package/out/zero-cache/src/server/anonymous-otel-start.d.ts +10 -1
  28. package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +1 -1
  29. package/out/zero-cache/src/server/anonymous-otel-start.js +34 -18
  30. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  31. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  32. package/out/zero-cache/src/server/syncer.js +1 -0
  33. package/out/zero-cache/src/server/syncer.js.map +1 -1
  34. package/out/zero-cache/src/services/analyze.d.ts +1 -1
  35. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  36. package/out/zero-cache/src/services/analyze.js +5 -5
  37. package/out/zero-cache/src/services/analyze.js.map +1 -1
  38. package/out/zero-cache/src/services/mutagen/pusher.d.ts +4 -4
  39. package/out/zero-cache/src/services/view-syncer/active-users-gauge.d.ts +2 -1
  40. package/out/zero-cache/src/services/view-syncer/active-users-gauge.d.ts.map +1 -1
  41. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +26 -13
  42. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  43. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts +1 -1
  44. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +1 -1
  45. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +39 -15
  46. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  47. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +4 -1
  48. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  49. package/out/zero-cache/src/services/view-syncer/cvr-store.js +31 -9
  50. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  51. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +3 -0
  52. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  53. package/out/zero-cache/src/services/view-syncer/cvr.js +11 -0
  54. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  55. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
  56. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  57. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +1 -2
  58. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  59. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +6 -6
  60. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  61. package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts +1 -0
  62. package/out/zero-cache/src/services/view-syncer/schema/cvr.d.ts.map +1 -1
  63. package/out/zero-cache/src/services/view-syncer/schema/cvr.js +23 -10
  64. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  65. package/out/zero-cache/src/services/view-syncer/schema/init.d.ts.map +1 -1
  66. package/out/zero-cache/src/services/view-syncer/schema/init.js +31 -1
  67. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  68. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +1 -0
  69. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  70. package/out/zero-cache/src/services/view-syncer/view-syncer.js +20 -16
  71. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  72. package/out/zero-cache/src/workers/connect-params.d.ts +1 -0
  73. package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
  74. package/out/zero-cache/src/workers/connect-params.js +2 -0
  75. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  76. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  77. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +2 -0
  78. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  79. package/out/zero-client/src/client/bindings.d.ts +12 -41
  80. package/out/zero-client/src/client/bindings.d.ts.map +1 -1
  81. package/out/zero-client/src/client/custom.d.ts +3 -0
  82. package/out/zero-client/src/client/custom.d.ts.map +1 -1
  83. package/out/zero-client/src/client/custom.js +3 -0
  84. package/out/zero-client/src/client/custom.js.map +1 -1
  85. package/out/zero-client/src/client/delete-clients-manager.d.ts +1 -1
  86. package/out/zero-client/src/client/delete-clients-manager.d.ts.map +1 -1
  87. package/out/zero-client/src/client/delete-clients-manager.js +30 -3
  88. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  89. package/out/zero-client/src/client/make-replicache-mutators.js +1 -3
  90. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  91. package/out/zero-client/src/client/options.d.ts +1 -1
  92. package/out/zero-client/src/client/options.js.map +1 -1
  93. package/out/zero-client/src/client/version.js +1 -1
  94. package/out/zero-client/src/client/zero.d.ts +1 -0
  95. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  96. package/out/zero-client/src/client/zero.js +43 -26
  97. package/out/zero-client/src/client/zero.js.map +1 -1
  98. package/out/zero-client/src/mod.d.ts +6 -4
  99. package/out/zero-client/src/mod.d.ts.map +1 -1
  100. package/out/zero-protocol/src/analyze-query-result.d.ts +2 -2
  101. package/out/zero-protocol/src/analyze-query-result.js +2 -2
  102. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  103. package/out/zero-protocol/src/down.d.ts +2 -2
  104. package/out/zero-protocol/src/inspect-down.d.ts +6 -6
  105. package/out/zero-protocol/src/inspect-up.d.ts +4 -4
  106. package/out/zero-protocol/src/inspect-up.js +1 -1
  107. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  108. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  109. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  110. package/out/zero-protocol/src/protocol-version.js +1 -1
  111. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  112. package/out/zero-protocol/src/up.d.ts +1 -1
  113. package/out/zero-react/src/bindings.d.ts +2 -0
  114. package/out/zero-react/src/bindings.d.ts.map +1 -0
  115. package/out/zero-react/src/mod.d.ts +1 -10
  116. package/out/zero-react/src/mod.d.ts.map +1 -1
  117. package/out/zero-react/src/{use-zero-connection-state.d.ts → use-connection-state.d.ts} +3 -3
  118. package/out/zero-react/src/use-connection-state.d.ts.map +1 -0
  119. package/out/zero-react/src/{use-zero-connection-state.js → use-connection-state.js} +3 -3
  120. package/out/zero-react/src/use-connection-state.js.map +1 -0
  121. package/out/zero-react/src/use-query.d.ts +2 -10
  122. package/out/zero-react/src/use-query.d.ts.map +1 -1
  123. package/out/zero-react/src/use-query.js +24 -22
  124. package/out/zero-react/src/use-query.js.map +1 -1
  125. package/out/zero-react/src/use-zero-online.d.ts +1 -1
  126. package/out/zero-react/src/use-zero-online.js.map +1 -1
  127. package/out/zero-react/src/zero-provider.d.ts +1 -5
  128. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  129. package/out/zero-react/src/zero-provider.js +16 -0
  130. package/out/zero-react/src/zero-provider.js.map +1 -1
  131. package/out/zero-react/src/zero.d.ts +2 -0
  132. package/out/zero-react/src/zero.d.ts.map +1 -0
  133. package/out/zero-schema/src/permissions.d.ts.map +1 -1
  134. package/out/zero-schema/src/permissions.js +2 -8
  135. package/out/zero-schema/src/permissions.js.map +1 -1
  136. package/out/zero-server/src/custom.d.ts +3 -0
  137. package/out/zero-server/src/custom.d.ts.map +1 -1
  138. package/out/zero-server/src/custom.js +3 -0
  139. package/out/zero-server/src/custom.js.map +1 -1
  140. package/out/zero-solid/src/bindings.d.ts +2 -0
  141. package/out/zero-solid/src/bindings.d.ts.map +1 -0
  142. package/out/zero-solid/src/mod.d.ts +1 -8
  143. package/out/zero-solid/src/mod.d.ts.map +1 -1
  144. package/out/zero-solid/src/solid-view.d.ts +1 -8
  145. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  146. package/out/zero-solid/src/solid-view.js +31 -0
  147. package/out/zero-solid/src/solid-view.js.map +1 -1
  148. package/out/zero-solid/src/{use-zero-connection-state.d.ts → use-connection-state.d.ts} +3 -3
  149. package/out/zero-solid/src/use-connection-state.d.ts.map +1 -0
  150. package/out/zero-solid/src/{use-zero-connection-state.js → use-connection-state.js} +3 -3
  151. package/out/zero-solid/src/use-connection-state.js.map +1 -0
  152. package/out/zero-solid/src/use-query.d.ts +1 -7
  153. package/out/zero-solid/src/use-query.d.ts.map +1 -1
  154. package/out/zero-solid/src/use-query.js +43 -12
  155. package/out/zero-solid/src/use-query.js.map +1 -1
  156. package/out/zero-solid/src/use-zero-online.d.ts +1 -1
  157. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  158. package/out/zero-solid/src/use-zero.d.ts +1 -5
  159. package/out/zero-solid/src/use-zero.d.ts.map +1 -1
  160. package/out/zero-solid/src/use-zero.js +16 -0
  161. package/out/zero-solid/src/use-zero.js.map +1 -1
  162. package/out/zero-solid/src/zero.d.ts +2 -0
  163. package/out/zero-solid/src/zero.d.ts.map +1 -0
  164. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  165. package/out/zql/src/ivm/flipped-join.js +29 -27
  166. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  167. package/out/zql/src/ivm/join-utils.d.ts +7 -1
  168. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  169. package/out/zql/src/ivm/join-utils.js +12 -0
  170. package/out/zql/src/ivm/join-utils.js.map +1 -1
  171. package/out/zql/src/ivm/join.d.ts.map +1 -1
  172. package/out/zql/src/ivm/join.js +11 -25
  173. package/out/zql/src/ivm/join.js.map +1 -1
  174. package/out/zql/src/mutate/custom.d.ts +3 -0
  175. package/out/zql/src/mutate/custom.d.ts.map +1 -1
  176. package/out/zql/src/mutate/custom.js.map +1 -1
  177. package/out/zql/src/mutate/mutator-registry.d.ts +2 -2
  178. package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
  179. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  180. package/out/zql/src/mutate/mutator.d.ts +1 -1
  181. package/out/zql/src/mutate/mutator.d.ts.map +1 -1
  182. package/out/zql/src/mutate/mutator.js.map +1 -1
  183. package/out/zql/src/planner/planner-debug.d.ts +3 -3
  184. package/out/zql/src/planner/planner-debug.js.map +1 -1
  185. package/out/zql/src/query/create-builder.d.ts +2 -1
  186. package/out/zql/src/query/create-builder.d.ts.map +1 -1
  187. package/out/zql/src/query/create-builder.js +3 -0
  188. package/out/zql/src/query/create-builder.js.map +1 -1
  189. package/out/zql/src/query/query-impl.d.ts +39 -6
  190. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  191. package/out/zql/src/query/query-impl.js +414 -23
  192. package/out/zql/src/query/query-impl.js.map +1 -1
  193. package/out/zql/src/query/query-registry.d.ts +2 -2
  194. package/out/zql/src/query/query-registry.d.ts.map +1 -1
  195. package/out/zql/src/query/query-registry.js.map +1 -1
  196. package/out/zql/src/query/runnable-query-impl.d.ts +2 -2
  197. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
  198. package/out/zql/src/query/runnable-query-impl.js +2 -2
  199. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  200. package/out/zql/src/query/schema-query.d.ts +4 -2
  201. package/out/zql/src/query/schema-query.d.ts.map +1 -1
  202. package/out/zql/src/query/static-query.d.ts +2 -16
  203. package/out/zql/src/query/static-query.d.ts.map +1 -1
  204. package/out/zql/src/query/static-query.js +10 -37
  205. package/out/zql/src/query/static-query.js.map +1 -1
  206. package/package.json +7 -3
  207. package/out/zero-client/src/client/bindings.js +0 -33
  208. package/out/zero-client/src/client/bindings.js.map +0 -1
  209. package/out/zero-react/src/components/inspector.d.ts +0 -9
  210. package/out/zero-react/src/components/inspector.d.ts.map +0 -1
  211. package/out/zero-react/src/components/inspector.js +0 -38
  212. package/out/zero-react/src/components/inspector.js.map +0 -1
  213. package/out/zero-react/src/components/mark-icon.d.ts +0 -3
  214. package/out/zero-react/src/components/mark-icon.d.ts.map +0 -1
  215. package/out/zero-react/src/components/mark-icon.js +0 -28
  216. package/out/zero-react/src/components/mark-icon.js.map +0 -1
  217. package/out/zero-react/src/components/zero-inspector.d.ts +0 -8
  218. package/out/zero-react/src/components/zero-inspector.d.ts.map +0 -1
  219. package/out/zero-react/src/components/zero-inspector.js +0 -44
  220. package/out/zero-react/src/components/zero-inspector.js.map +0 -1
  221. package/out/zero-react/src/use-zero-connection-state.d.ts.map +0 -1
  222. package/out/zero-react/src/use-zero-connection-state.js.map +0 -1
  223. package/out/zero-solid/src/use-zero-connection-state.d.ts.map +0 -1
  224. package/out/zero-solid/src/use-zero-connection-state.js.map +0 -1
  225. package/out/zql/src/query/abstract-query.d.ts +0 -42
  226. package/out/zql/src/query/abstract-query.d.ts.map +0 -1
  227. package/out/zql/src/query/abstract-query.js +0 -405
  228. package/out/zql/src/query/abstract-query.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,oDAAoD,CAAC;AAC3F,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,kDAAkD,CAAC;AAWxF,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAIjE,OAAO,EAAiB,KAAK,SAAS,EAAC,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6CAA6C,CAAC;AAI9E,wBAAsB,YAAY,CAChC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,oBAAoB,EAC5B,YAAY,EAAE,YAAY,EAC1B,GAAG,EAAE,GAAG,EACR,UAAU,UAAO,EACjB,UAAU,UAAQ,EAClB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,QAAQ,CAAC,EAAE,SAAS,EACpB,YAAY,UAAQ,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAwE7B"}
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/services/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,oDAAoD,CAAC;AAC3F,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,kDAAkD,CAAC;AAWxF,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAIjE,OAAO,EAAiB,KAAK,SAAS,EAAC,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,6CAA6C,CAAC;AAI9E,wBAAsB,YAAY,CAChC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,oBAAoB,EAC5B,YAAY,EAAE,YAAY,EAC1B,GAAG,EAAE,GAAG,EACR,UAAU,UAAO,EACjB,UAAU,UAAQ,EAClB,WAAW,CAAC,EAAE,iBAAiB,EAC/B,QAAQ,CAAC,EAAE,SAAS,EACpB,SAAS,UAAQ,GAChB,OAAO,CAAC,kBAAkB,CAAC,CAwE7B"}
@@ -54,7 +54,7 @@ import { computeZqlSpecs, mustGetTableSpec } from "../db/lite-tables.js";
54
54
  import { runAst } from "./run-ast.js";
55
55
  import { TimeSliceTimer } from "./view-syncer/view-syncer.js";
56
56
  const TIME_SLICE_LAP_THRESHOLD_MS = 200;
57
- async function analyzeQuery(lc, config, clientSchema, ast, syncedRows = true, vendedRows = false, permissions, authData, plannerDebug = false) {
57
+ async function analyzeQuery(lc, config, clientSchema, ast, syncedRows = true, vendedRows = false, permissions, authData, joinPlans = false) {
58
58
  var _stack = [];
59
59
  try {
60
60
  const db = __using(_stack, new Database(lc, config.replica.file));
@@ -62,8 +62,8 @@ async function analyzeQuery(lc, config, clientSchema, ast, syncedRows = true, ve
62
62
  const tableSpecs = /* @__PURE__ */ new Map();
63
63
  const tables = /* @__PURE__ */ new Map();
64
64
  computeZqlSpecs(lc, db, tableSpecs, fullTables);
65
- const planDebugger = plannerDebug ? new AccumulatorDebugger() : void 0;
66
- const costModel = plannerDebug ? createSQLiteCostModel(db, tableSpecs) : void 0;
65
+ const planDebugger = joinPlans ? new AccumulatorDebugger() : void 0;
66
+ const costModel = joinPlans ? createSQLiteCostModel(db, tableSpecs) : void 0;
67
67
  const timer = await new TimeSliceTimer().start();
68
68
  const shouldYield = () => timer.elapsedLap() > TIME_SLICE_LAP_THRESHOLD_MS;
69
69
  const yieldProcess = () => timer.yieldProcess();
@@ -113,9 +113,9 @@ async function analyzeQuery(lc, config, clientSchema, ast, syncedRows = true, ve
113
113
  },
114
114
  yieldProcess
115
115
  );
116
- result.plans = explainQueries(result.readRowCountsByQuery ?? {}, db);
116
+ result.sqlitePlans = explainQueries(result.readRowCountsByQuery ?? {}, db);
117
117
  if (planDebugger) {
118
- result.plannerEvents = serializePlanDebugEvents(planDebugger.events);
118
+ result.joinPlans = serializePlanDebugEvents(planDebugger.events);
119
119
  }
120
120
  return result;
121
121
  } catch (_) {
@@ -1 +1 @@
1
- {"version":3,"file":"analyze.js","sources":["../../../../../zero-cache/src/services/analyze.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {AnalyzeQueryResult} from '../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {PermissionsConfig} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {Debug} from '../../../zql/src/builder/debug-delegate.ts';\nimport {MemoryStorage} from '../../../zql/src/ivm/memory-storage.ts';\nimport {\n AccumulatorDebugger,\n serializePlanDebugEvents,\n} from '../../../zql/src/planner/planner-debug.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport {explainQueries} from '../../../zqlite/src/explain-queries.ts';\nimport {createSQLiteCostModel} from '../../../zqlite/src/sqlite-cost-model.ts';\nimport {TableSource} from '../../../zqlite/src/table-source.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {computeZqlSpecs, mustGetTableSpec} from '../db/lite-tables.ts';\nimport type {LiteAndZqlSpec, LiteTableSpec} from '../db/specs.ts';\nimport {runAst} from './run-ast.ts';\nimport {TimeSliceTimer, type TokenData} from './view-syncer/view-syncer.ts';\nimport type {ClientSchema} from '../../../zero-protocol/src/client-schema.ts';\n\nconst TIME_SLICE_LAP_THRESHOLD_MS = 200;\n\nexport async function analyzeQuery(\n lc: LogContext,\n config: NormalizedZeroConfig,\n clientSchema: ClientSchema,\n ast: AST,\n syncedRows = true,\n vendedRows = false,\n permissions?: PermissionsConfig,\n authData?: TokenData,\n plannerDebug = false,\n): Promise<AnalyzeQueryResult> {\n using db = new Database(lc, config.replica.file);\n const fullTables = new Map<string, LiteTableSpec>();\n const tableSpecs = new Map<string, LiteAndZqlSpec>();\n const tables = new Map<string, TableSource>();\n\n computeZqlSpecs(lc, db, tableSpecs, fullTables);\n\n const planDebugger = plannerDebug ? new AccumulatorDebugger() : undefined;\n const costModel = plannerDebug\n ? createSQLiteCostModel(db, tableSpecs)\n : undefined;\n const timer = await new TimeSliceTimer().start();\n const shouldYield = () => timer.elapsedLap() > TIME_SLICE_LAP_THRESHOLD_MS;\n const yieldProcess = () => timer.yieldProcess();\n const result = await runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: permissions !== undefined,\n syncedRows,\n vendedRows,\n authData,\n db,\n tableSpecs,\n permissions,\n costModel,\n planDebugger,\n host: {\n debug: new Debug(),\n getSource(tableName: string) {\n let source = tables.get(tableName);\n if (source) {\n return source;\n }\n\n const tableSpec = mustGetTableSpec(tableSpecs, tableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n config.log,\n db,\n tableName,\n tableSpec.zqlSpec,\n primaryKey,\n shouldYield,\n );\n tables.set(tableName, source);\n return source;\n },\n createStorage() {\n return new MemoryStorage();\n },\n decorateSourceInput: input => input,\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n },\n yieldProcess,\n );\n\n result.plans = explainQueries(result.readRowCountsByQuery ?? {}, db);\n\n if (planDebugger) {\n result.plannerEvents = serializePlanDebugEvents(planDebugger.events);\n }\n\n return result;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,MAAM,8BAA8B;AAEpC,eAAsB,aACpB,IACA,QACA,cACA,KACA,aAAa,MACb,aAAa,OACb,aACA,UACA,eAAe,OACc;AAC7B;AAAA;AAAA,UAAM,KAAK,oBAAI,SAAS,IAAI,OAAO,QAAQ,IAAI;AAC/C,UAAM,iCAAiB,IAAA;AACvB,UAAM,iCAAiB,IAAA;AACvB,UAAM,6BAAa,IAAA;AAEnB,oBAAgB,IAAI,IAAI,YAAY,UAAU;AAE9C,UAAM,eAAe,eAAe,IAAI,oBAAA,IAAwB;AAChE,UAAM,YAAY,eACd,sBAAsB,IAAI,UAAU,IACpC;AACJ,UAAM,QAAQ,MAAM,IAAI,eAAA,EAAiB,MAAA;AACzC,UAAM,cAAc,MAAM,MAAM,WAAA,IAAe;AAC/C,UAAM,eAAe,MAAM,MAAM,aAAA;AACjC,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE,kBAAkB,gBAAgB;AAAA,QAClC;AAAA,QACA;AAAA,QACA;AAAA,QAGA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,UACJ,OAAO,IAAI,MAAA;AAAA,UACX,UAAU,WAAmB;AAC3B,gBAAI,SAAS,OAAO,IAAI,SAAS;AACjC,gBAAI,QAAQ;AACV,qBAAO;AAAA,YACT;AAEA,kBAAM,YAAY,iBAAiB,YAAY,SAAS;AACxD,kBAAM,EAAC,eAAc,UAAU;AAE/B,qBAAS,IAAI;AAAA,cACX;AAAA,cACA,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA,UAAU;AAAA,cACV;AAAA,cACA;AAAA,YAAA;AAEF,mBAAO,IAAI,WAAW,MAAM;AAC5B,mBAAO;AAAA,UACT;AAAA,UACA,gBAAgB;AACd,mBAAO,IAAI,cAAA;AAAA,UACb;AAAA,UACA,qBAAqB,CAAA,UAAS;AAAA,UAC9B,eAAe,CAAA,UAAS;AAAA,UACxB,UAAU;AAAA,UAAC;AAAA,UACX,qBAAqB,CAAA,UAAS;AAAA,QAAA;AAAA,MAChC;AAAA,MAEF;AAAA,IAAA;AAGF,WAAO,QAAQ,eAAe,OAAO,wBAAwB,CAAA,GAAI,EAAE;AAEnE,QAAI,cAAc;AAChB,aAAO,gBAAgB,yBAAyB,aAAa,MAAM;AAAA,IACrE;AAEA,WAAO;AAAA,WAtEP;AAAA;AAAA;AAAA;AAAA;AAuEF;"}
1
+ {"version":3,"file":"analyze.js","sources":["../../../../../zero-cache/src/services/analyze.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {AnalyzeQueryResult} from '../../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../../zero-protocol/src/ast.ts';\nimport type {PermissionsConfig} from '../../../zero-schema/src/compiled-permissions.ts';\nimport {Debug} from '../../../zql/src/builder/debug-delegate.ts';\nimport {MemoryStorage} from '../../../zql/src/ivm/memory-storage.ts';\nimport {\n AccumulatorDebugger,\n serializePlanDebugEvents,\n} from '../../../zql/src/planner/planner-debug.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\nimport {explainQueries} from '../../../zqlite/src/explain-queries.ts';\nimport {createSQLiteCostModel} from '../../../zqlite/src/sqlite-cost-model.ts';\nimport {TableSource} from '../../../zqlite/src/table-source.ts';\nimport type {NormalizedZeroConfig} from '../config/normalize.ts';\nimport {computeZqlSpecs, mustGetTableSpec} from '../db/lite-tables.ts';\nimport type {LiteAndZqlSpec, LiteTableSpec} from '../db/specs.ts';\nimport {runAst} from './run-ast.ts';\nimport {TimeSliceTimer, type TokenData} from './view-syncer/view-syncer.ts';\nimport type {ClientSchema} from '../../../zero-protocol/src/client-schema.ts';\n\nconst TIME_SLICE_LAP_THRESHOLD_MS = 200;\n\nexport async function analyzeQuery(\n lc: LogContext,\n config: NormalizedZeroConfig,\n clientSchema: ClientSchema,\n ast: AST,\n syncedRows = true,\n vendedRows = false,\n permissions?: PermissionsConfig,\n authData?: TokenData,\n joinPlans = false,\n): Promise<AnalyzeQueryResult> {\n using db = new Database(lc, config.replica.file);\n const fullTables = new Map<string, LiteTableSpec>();\n const tableSpecs = new Map<string, LiteAndZqlSpec>();\n const tables = new Map<string, TableSource>();\n\n computeZqlSpecs(lc, db, tableSpecs, fullTables);\n\n const planDebugger = joinPlans ? new AccumulatorDebugger() : undefined;\n const costModel = joinPlans\n ? createSQLiteCostModel(db, tableSpecs)\n : undefined;\n const timer = await new TimeSliceTimer().start();\n const shouldYield = () => timer.elapsedLap() > TIME_SLICE_LAP_THRESHOLD_MS;\n const yieldProcess = () => timer.yieldProcess();\n const result = await runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: permissions !== undefined,\n syncedRows,\n vendedRows,\n authData,\n db,\n tableSpecs,\n permissions,\n costModel,\n planDebugger,\n host: {\n debug: new Debug(),\n getSource(tableName: string) {\n let source = tables.get(tableName);\n if (source) {\n return source;\n }\n\n const tableSpec = mustGetTableSpec(tableSpecs, tableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n config.log,\n db,\n tableName,\n tableSpec.zqlSpec,\n primaryKey,\n shouldYield,\n );\n tables.set(tableName, source);\n return source;\n },\n createStorage() {\n return new MemoryStorage();\n },\n decorateSourceInput: input => input,\n decorateInput: input => input,\n addEdge() {},\n decorateFilterInput: input => input,\n },\n },\n yieldProcess,\n );\n\n result.sqlitePlans = explainQueries(result.readRowCountsByQuery ?? {}, db);\n\n if (planDebugger) {\n result.joinPlans = serializePlanDebugEvents(planDebugger.events);\n }\n\n return result;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqBA,MAAM,8BAA8B;AAEpC,eAAsB,aACpB,IACA,QACA,cACA,KACA,aAAa,MACb,aAAa,OACb,aACA,UACA,YAAY,OACiB;AAC7B;AAAA;AAAA,UAAM,KAAK,oBAAI,SAAS,IAAI,OAAO,QAAQ,IAAI;AAC/C,UAAM,iCAAiB,IAAA;AACvB,UAAM,iCAAiB,IAAA;AACvB,UAAM,6BAAa,IAAA;AAEnB,oBAAgB,IAAI,IAAI,YAAY,UAAU;AAE9C,UAAM,eAAe,YAAY,IAAI,oBAAA,IAAwB;AAC7D,UAAM,YAAY,YACd,sBAAsB,IAAI,UAAU,IACpC;AACJ,UAAM,QAAQ,MAAM,IAAI,eAAA,EAAiB,MAAA;AACzC,UAAM,cAAc,MAAM,MAAM,WAAA,IAAe;AAC/C,UAAM,eAAe,MAAM,MAAM,aAAA;AACjC,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,QACE,kBAAkB,gBAAgB;AAAA,QAClC;AAAA,QACA;AAAA,QACA;AAAA,QAGA;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM;AAAA,UACJ,OAAO,IAAI,MAAA;AAAA,UACX,UAAU,WAAmB;AAC3B,gBAAI,SAAS,OAAO,IAAI,SAAS;AACjC,gBAAI,QAAQ;AACV,qBAAO;AAAA,YACT;AAEA,kBAAM,YAAY,iBAAiB,YAAY,SAAS;AACxD,kBAAM,EAAC,eAAc,UAAU;AAE/B,qBAAS,IAAI;AAAA,cACX;AAAA,cACA,OAAO;AAAA,cACP;AAAA,cACA;AAAA,cACA,UAAU;AAAA,cACV;AAAA,cACA;AAAA,YAAA;AAEF,mBAAO,IAAI,WAAW,MAAM;AAC5B,mBAAO;AAAA,UACT;AAAA,UACA,gBAAgB;AACd,mBAAO,IAAI,cAAA;AAAA,UACb;AAAA,UACA,qBAAqB,CAAA,UAAS;AAAA,UAC9B,eAAe,CAAA,UAAS;AAAA,UACxB,UAAU;AAAA,UAAC;AAAA,UACX,qBAAqB,CAAA,UAAS;AAAA,QAAA;AAAA,MAChC;AAAA,MAEF;AAAA,IAAA;AAGF,WAAO,cAAc,eAAe,OAAO,wBAAwB,CAAA,GAAI,EAAE;AAEzE,QAAI,cAAc;AAChB,aAAO,YAAY,yBAAyB,aAAa,MAAM;AAAA,IACjE;AAEA,WAAO;AAAA,WAtEP;AAAA;AAAA;AAAA;AAAA;AAuEF;"}
@@ -135,12 +135,12 @@ export declare class PusherService implements Service, Pusher {
135
135
  afterPermissions?: string | undefined;
136
136
  vendedRowCounts?: Record<string, Record<string, number>> | undefined;
137
137
  vendedRows?: Record<string, Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]>> | undefined;
138
- plans?: Record<string, string[]> | undefined;
138
+ sqlitePlans?: Record<string, string[]> | undefined;
139
139
  readRows?: Record<string, Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]>> | undefined;
140
140
  readRowCountsByQuery?: Record<string, Record<string, number>> | undefined;
141
141
  readRowCount?: number | undefined;
142
142
  dbScansByQuery?: Record<string, Record<string, number>> | undefined;
143
- plannerEvents?: ({
143
+ joinPlans?: ({
144
144
  type: "attempt-start";
145
145
  attemptNumber: number;
146
146
  totalAttempts: number;
@@ -434,12 +434,12 @@ export declare class PusherService implements Service, Pusher {
434
434
  afterPermissions?: string | undefined;
435
435
  vendedRowCounts?: Record<string, Record<string, number>> | undefined;
436
436
  vendedRows?: Record<string, Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]>> | undefined;
437
- plans?: Record<string, string[]> | undefined;
437
+ sqlitePlans?: Record<string, string[]> | undefined;
438
438
  readRows?: Record<string, Record<string, Readonly<Record<string, import("../../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>[]>> | undefined;
439
439
  readRowCountsByQuery?: Record<string, Record<string, number>> | undefined;
440
440
  readRowCount?: number | undefined;
441
441
  dbScansByQuery?: Record<string, Record<string, number>> | undefined;
442
- plannerEvents?: ({
442
+ joinPlans?: ({
443
443
  type: "attempt-start";
444
444
  attemptNumber: number;
445
445
  totalAttempts: number;
@@ -1,4 +1,5 @@
1
1
  import type { LogContext } from '@rocicorp/logger';
2
+ import { type ActiveUsers } from '../../server/anonymous-otel-start.ts';
2
3
  import type { PostgresDB } from '../../types/pg.ts';
3
4
  import { type ShardID } from '../../types/shards.ts';
4
5
  import type { Service } from '../service.ts';
@@ -8,7 +9,7 @@ type Options = {
8
9
  export declare class ActiveUsersGauge implements Service {
9
10
  #private;
10
11
  readonly id = "active-users-gauge";
11
- constructor(lc: LogContext, db: PostgresDB, shard: ShardID, opts?: Options);
12
+ constructor(lc: LogContext, db: PostgresDB, shard: ShardID, opts?: Options, setActiveUsersGetterFn?: (getter: () => ActiveUsers) => void);
12
13
  run(): Promise<void>;
13
14
  stop(): Promise<void>;
14
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"active-users-gauge.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/active-users-gauge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAY,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAK3C,KAAK,OAAO,GAAG;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,qBAAa,gBAAiB,YAAW,OAAO;;IAC9C,QAAQ,CAAC,EAAE,wBAAwB;gBAajC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,OAAO,EACd,IAAI,GAAE,OAAY;IAQd,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B1B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAItB"}
1
+ {"version":3,"file":"active-users-gauge.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/active-users-gauge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAY,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAK3C,KAAK,OAAO,GAAG;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,qBAAa,gBAAiB,YAAW,OAAO;;IAC9C,QAAQ,CAAC,EAAE,wBAAwB;gBAajC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,OAAO,EACd,IAAI,GAAE,OAAY,EAClB,sBAAsB,sCAAuB;IASzC,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA8C1B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAItB"}
@@ -1,3 +1,5 @@
1
+ import { must } from "../../../../shared/src/must.js";
2
+ import { mapValues } from "../../../../shared/src/objects.js";
1
3
  import { setActiveUsersGetter } from "../../server/anonymous-otel-start.js";
2
4
  import { cvrSchema } from "../../types/shards.js";
3
5
  import { RunningState } from "../running-state.js";
@@ -10,30 +12,41 @@ class ActiveUsersGauge {
10
12
  #schema;
11
13
  #updateIntervalMs;
12
14
  #state = new RunningState("active-users-gauge");
15
+ #setActiveUsersGetter;
13
16
  // latest computed value exposed via the observable gauge callback
14
- #value = 0;
15
- #getterSet = false;
16
- constructor(lc, db, shard, opts = {}) {
17
+ #lastActiveUsers;
18
+ constructor(lc, db, shard, opts = {}, setActiveUsersGetterFn = setActiveUsersGetter) {
17
19
  this.#lc = lc;
18
20
  this.#db = db;
19
21
  this.#schema = cvrSchema(shard);
20
22
  this.#updateIntervalMs = opts.updateIntervalMs ?? 60 * 1e3;
23
+ this.#setActiveUsersGetter = setActiveUsersGetterFn;
21
24
  }
22
25
  async run() {
23
26
  while (this.#state.shouldRun()) {
24
27
  try {
25
- const since = Date.now() - DAY;
26
- const [{ cnt }] = await this.#db`
27
- SELECT COUNT(*) AS cnt
28
- FROM ${this.#db(this.#schema)}.instances
29
- WHERE "lastActive" > ${since}
28
+ const now = Date.now();
29
+ const since30day = now - DAY * 30;
30
+ const since7day = now - DAY * 7;
31
+ const since1day = now - DAY;
32
+ const [actives] = await this.#db`
33
+ SELECT
34
+ COUNT(*) FILTER (WHERE "lastActive" >= ${since1day}) AS active_users_last_day,
35
+ COUNT(DISTINCT("profileID")) FILTER (WHERE "lastActive" >= ${since1day} AND starts_with("profileID", 'p')) AS users_1da,
36
+ COUNT(DISTINCT("profileID")) FILTER (WHERE "lastActive" >= ${since7day} AND starts_with("profileID", 'p')) AS users_7da,
37
+ COUNT(DISTINCT("profileID")) FILTER (WHERE starts_with("profileID", 'p')) AS users_30da,
38
+ COUNT(DISTINCT("profileID")) FILTER (WHERE "lastActive" >= ${since1day}) AS users_1da_legacy,
39
+ COUNT(DISTINCT("profileID")) FILTER (WHERE "lastActive" >= ${since7day}) AS users_7da_legacy,
40
+ COUNT(DISTINCT("profileID")) AS users_30da_legacy
41
+ FROM ${this.#db(this.#schema)}.instances
42
+ WHERE "lastActive" >= ${since30day}
30
43
  `;
31
- this.#value = Number(cnt);
32
- if (!this.#getterSet) {
33
- setActiveUsersGetter(() => this.#value);
34
- this.#getterSet = true;
44
+ const setGetter = this.#lastActiveUsers === void 0;
45
+ this.#lastActiveUsers = mapValues(actives, (bigVal) => Number(bigVal));
46
+ if (setGetter) {
47
+ this.#setActiveUsersGetter(() => must(this.#lastActiveUsers));
35
48
  }
36
- this.#lc.debug?.(`updated active-users gauge to ${this.#value}`);
49
+ this.#lc.debug?.(`updated active-users gauge`, this.#lastActiveUsers);
37
50
  } catch (e) {
38
51
  this.#lc.warn?.("error updating active-users gauge", e);
39
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"active-users-gauge.js","sources":["../../../../../../zero-cache/src/services/view-syncer/active-users-gauge.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {setActiveUsersGetter} from '../../server/anonymous-otel-start.ts';\nimport type {PostgresDB} from '../../types/pg.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {Service} from '../service.ts';\n\nconst MINUTE = 60 * 1000;\nconst DAY = 24 * 60 * MINUTE;\n\ntype Options = {\n updateIntervalMs?: number;\n};\n\nexport class ActiveUsersGauge implements Service {\n readonly id = 'active-users-gauge';\n\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #updateIntervalMs: number;\n readonly #state = new RunningState('active-users-gauge');\n\n // latest computed value exposed via the observable gauge callback\n #value = 0;\n #getterSet = false;\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n opts: Options = {},\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#updateIntervalMs = opts.updateIntervalMs ?? 60 * 1000; // default 1 minute\n }\n\n async run(): Promise<void> {\n while (this.#state.shouldRun()) {\n try {\n const since = Date.now() - DAY;\n const [{cnt}] = await this.#db<[{cnt: bigint}]>`\n SELECT COUNT(*) AS cnt\n FROM ${this.#db(this.#schema)}.instances\n WHERE \"lastActive\" > ${since}\n `;\n this.#value = Number(cnt);\n\n // Set the getter after the first value is computed\n if (!this.#getterSet) {\n setActiveUsersGetter(() => this.#value);\n this.#getterSet = true;\n }\n\n this.#lc.debug?.(`updated active-users gauge to ${this.#value}`);\n } catch (e) {\n this.#lc.warn?.('error updating active-users gauge', e);\n }\n\n await this.#state.sleep(this.#updateIntervalMs);\n }\n }\n\n stop(): Promise<void> {\n this.#state.stop(this.#lc);\n return this.#state.stopped();\n }\n}\n"],"names":[],"mappings":";;;AAOA,MAAM,SAAS,KAAK;AACpB,MAAM,MAAM,KAAK,KAAK;AAMf,MAAM,iBAAoC;AAAA,EACtC,KAAK;AAAA,EAEL;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,IAAI,aAAa,oBAAoB;AAAA;AAAA,EAGvD,SAAS;AAAA,EACT,aAAa;AAAA,EAEb,YACE,IACA,IACA,OACA,OAAgB,CAAA,GAChB;AACA,SAAK,MAAM;AACX,SAAK,MAAM;AACX,SAAK,UAAU,UAAU,KAAK;AAC9B,SAAK,oBAAoB,KAAK,oBAAoB,KAAK;AAAA,EACzD;AAAA,EAEA,MAAM,MAAqB;AACzB,WAAO,KAAK,OAAO,aAAa;AAC9B,UAAI;AACF,cAAM,QAAQ,KAAK,IAAA,IAAQ;AAC3B,cAAM,CAAC,EAAC,IAAA,CAAI,IAAI,MAAM,KAAK;AAAA;AAAA,iBAElB,KAAK,IAAI,KAAK,OAAO,CAAC;AAAA,iCACN,KAAK;AAAA;AAE9B,aAAK,SAAS,OAAO,GAAG;AAGxB,YAAI,CAAC,KAAK,YAAY;AACpB,+BAAqB,MAAM,KAAK,MAAM;AACtC,eAAK,aAAa;AAAA,QACpB;AAEA,aAAK,IAAI,QAAQ,iCAAiC,KAAK,MAAM,EAAE;AAAA,MACjE,SAAS,GAAG;AACV,aAAK,IAAI,OAAO,qCAAqC,CAAC;AAAA,MACxD;AAEA,YAAM,KAAK,OAAO,MAAM,KAAK,iBAAiB;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,OAAsB;AACpB,SAAK,OAAO,KAAK,KAAK,GAAG;AACzB,WAAO,KAAK,OAAO,QAAA;AAAA,EACrB;AACF;"}
1
+ {"version":3,"file":"active-users-gauge.js","sources":["../../../../../../zero-cache/src/services/view-syncer/active-users-gauge.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {must} from '../../../../shared/src/must.ts';\nimport {mapValues} from '../../../../shared/src/objects.ts';\nimport {\n setActiveUsersGetter,\n type ActiveUsers,\n} from '../../server/anonymous-otel-start.ts';\nimport type {PostgresDB} from '../../types/pg.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {Service} from '../service.ts';\n\nconst MINUTE = 60 * 1000;\nconst DAY = 24 * 60 * MINUTE;\n\ntype Options = {\n updateIntervalMs?: number;\n};\n\nexport class ActiveUsersGauge implements Service {\n readonly id = 'active-users-gauge';\n\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #updateIntervalMs: number;\n readonly #state = new RunningState('active-users-gauge');\n readonly #setActiveUsersGetter: typeof setActiveUsersGetter;\n\n // latest computed value exposed via the observable gauge callback\n #lastActiveUsers: ActiveUsers | undefined;\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n opts: Options = {},\n setActiveUsersGetterFn = setActiveUsersGetter,\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#updateIntervalMs = opts.updateIntervalMs ?? 60 * 1000; // default 1 minute\n this.#setActiveUsersGetter = setActiveUsersGetterFn;\n }\n\n async run(): Promise<void> {\n while (this.#state.shouldRun()) {\n try {\n const now = Date.now();\n const since30day = now - DAY * 30;\n const since7day = now - DAY * 7;\n const since1day = now - DAY;\n\n // This query performs a single scan over the `profile_ids_last_active`\n // index to compute aggregated results for all of our active user\n // metric variants.\n //\n // The eventually-correct metrics are `users_#da` which count distinct\n // profileIDs produced by the zero-client (i.e. starting with 'p').\n // The `users_#da_legacy` metrics include back-filled profileIDs (which\n // start with `cg`) and will over-count users on apps using memstore.\n const [actives] = await this.#db<[ActiveUsers]> /*sql*/ `\n SELECT \n COUNT(*) FILTER (WHERE \"lastActive\" >= ${since1day}) AS active_users_last_day,\n COUNT(DISTINCT(\"profileID\")) FILTER (WHERE \"lastActive\" >= ${since1day} AND starts_with(\"profileID\", 'p')) AS users_1da,\n COUNT(DISTINCT(\"profileID\")) FILTER (WHERE \"lastActive\" >= ${since7day} AND starts_with(\"profileID\", 'p')) AS users_7da,\n COUNT(DISTINCT(\"profileID\")) FILTER (WHERE starts_with(\"profileID\", 'p')) AS users_30da,\n COUNT(DISTINCT(\"profileID\")) FILTER (WHERE \"lastActive\" >= ${since1day}) AS users_1da_legacy,\n COUNT(DISTINCT(\"profileID\")) FILTER (WHERE \"lastActive\" >= ${since7day}) AS users_7da_legacy,\n COUNT(DISTINCT(\"profileID\")) AS users_30da_legacy\n FROM ${this.#db(this.#schema)}.instances\n WHERE \"lastActive\" >= ${since30day}\n `;\n // Determine if the getter needs to be set (i.e. the first time).\n const setGetter = this.#lastActiveUsers === undefined;\n this.#lastActiveUsers = mapValues(actives, bigVal => Number(bigVal));\n\n // Set the getter after the first value is computed\n if (setGetter) {\n this.#setActiveUsersGetter(() => must(this.#lastActiveUsers));\n }\n\n this.#lc.debug?.(`updated active-users gauge`, this.#lastActiveUsers);\n } catch (e) {\n this.#lc.warn?.('error updating active-users gauge', e);\n }\n\n await this.#state.sleep(this.#updateIntervalMs);\n }\n }\n\n stop(): Promise<void> {\n this.#state.stop(this.#lc);\n return this.#state.stopped();\n }\n}\n"],"names":[],"mappings":";;;;;AAYA,MAAM,SAAS,KAAK;AACpB,MAAM,MAAM,KAAK,KAAK;AAMf,MAAM,iBAAoC;AAAA,EACtC,KAAK;AAAA,EAEL;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,IAAI,aAAa,oBAAoB;AAAA,EAC9C;AAAA;AAAA,EAGT;AAAA,EAEA,YACE,IACA,IACA,OACA,OAAgB,CAAA,GAChB,yBAAyB,sBACzB;AACA,SAAK,MAAM;AACX,SAAK,MAAM;AACX,SAAK,UAAU,UAAU,KAAK;AAC9B,SAAK,oBAAoB,KAAK,oBAAoB,KAAK;AACvD,SAAK,wBAAwB;AAAA,EAC/B;AAAA,EAEA,MAAM,MAAqB;AACzB,WAAO,KAAK,OAAO,aAAa;AAC9B,UAAI;AACF,cAAM,MAAM,KAAK,IAAA;AACjB,cAAM,aAAa,MAAM,MAAM;AAC/B,cAAM,YAAY,MAAM,MAAM;AAC9B,cAAM,YAAY,MAAM;AAUxB,cAAM,CAAC,OAAO,IAAI,MAAM,KAAK;AAAA;AAAA,qDAEgB,SAAS;AAAA,yEACW,SAAS;AAAA,yEACT,SAAS;AAAA;AAAA,yEAET,SAAS;AAAA,yEACT,SAAS;AAAA;AAAA,mBAE/D,KAAK,IAAI,KAAK,OAAO,CAAC;AAAA,oCACL,UAAU;AAAA;AAGtC,cAAM,YAAY,KAAK,qBAAqB;AAC5C,aAAK,mBAAmB,UAAU,SAAS,CAAA,WAAU,OAAO,MAAM,CAAC;AAGnE,YAAI,WAAW;AACb,eAAK,sBAAsB,MAAM,KAAK,KAAK,gBAAgB,CAAC;AAAA,QAC9D;AAEA,aAAK,IAAI,QAAQ,8BAA8B,KAAK,gBAAgB;AAAA,MACtE,SAAS,GAAG;AACV,aAAK,IAAI,OAAO,qCAAqC,CAAC;AAAA,MACxD;AAEA,YAAM,KAAK,OAAO,MAAM,KAAK,iBAAiB;AAAA,IAChD;AAAA,EACF;AAAA,EAEA,OAAsB;AACpB,SAAK,OAAO,KAAK,KAAK,GAAG;AACzB,WAAO,KAAK,OAAO,QAAA;AAAA,EACrB;AACF;"}
@@ -10,7 +10,7 @@ type Options = {
10
10
  export declare class CVRPurger implements Service {
11
11
  #private;
12
12
  readonly id = "reaper";
13
- constructor(lc: LogContext, db: PostgresDB, shard: ShardID, { inactivityThresholdMs, initialBatchSize, initialIntervalMs }: Options);
13
+ constructor(lc: LogContext, db: PostgresDB, shard: ShardID, { inactivityThresholdMs, initialBatchSize, initialIntervalMs }: Options, tombstonePurgeThreshold?: number);
14
14
  run(): Promise<void>;
15
15
  purgeInactiveCVRs(maxCVRs: number): Promise<{
16
16
  purged: number;
@@ -1 +1 @@
1
- {"version":3,"file":"cvr-purger.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-purger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAA0B,KAAK,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAY,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAK3C,KAAK,OAAO,GAAG;IACb,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,qBAAa,SAAU,YAAW,OAAO;;IACvC,QAAQ,CAAC,EAAE,YAAY;gBAWrB,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,OAAO,EACd,EAAC,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,EAAC,EAAE,OAAO;IAUjE,GAAG;IAgDT,iBAAiB,CACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,CAAC;IAwD/C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAItB"}
1
+ {"version":3,"file":"cvr-purger.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-purger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAA0B,KAAK,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAY,KAAK,OAAO,EAAC,MAAM,uBAAuB,CAAC;AAE9D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,eAAe,CAAC;AAQ3C,KAAK,OAAO,GAAG;IACb,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,qBAAa,SAAU,YAAW,OAAO;;IACvC,QAAQ,CAAC,EAAE,YAAY;gBAYrB,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,OAAO,EACd,EAAC,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,EAAC,EAAE,OAAO,EACrE,uBAAuB,SAA4B;IAc/C,GAAG;IAgDT,iBAAiB,CACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,CAAC;IAgF/C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAItB"}
@@ -5,20 +5,26 @@ import { cvrSchema } from "../../types/shards.js";
5
5
  import { RunningState } from "../running-state.js";
6
6
  const MINUTE = 60 * 1e3;
7
7
  const MAX_PURGE_INTERVAL_MS = 16 * MINUTE;
8
+ const TOMBSTONE_PURGE_THRESHOLD = 31 * 24 * 60 * 60 * 1e3;
8
9
  class CVRPurger {
9
10
  id = "reaper";
10
11
  #lc;
11
12
  #db;
12
13
  #schema;
13
14
  #inactivityThresholdMs;
15
+ #tombstonePurgeThresholdMs;
14
16
  #initialBatchSize;
15
17
  #initialIntervalMs;
16
18
  #state = new RunningState("reaper");
17
- constructor(lc, db, shard, { inactivityThresholdMs, initialBatchSize, initialIntervalMs }) {
19
+ constructor(lc, db, shard, { inactivityThresholdMs, initialBatchSize, initialIntervalMs }, tombstonePurgeThreshold = TOMBSTONE_PURGE_THRESHOLD) {
18
20
  this.#lc = lc;
19
21
  this.#db = db;
20
22
  this.#schema = cvrSchema(shard);
21
23
  this.#inactivityThresholdMs = inactivityThresholdMs;
24
+ this.#tombstonePurgeThresholdMs = Math.max(
25
+ tombstonePurgeThreshold,
26
+ inactivityThresholdMs
27
+ );
22
28
  this.#initialBatchSize = initialBatchSize;
23
29
  this.#initialIntervalMs = initialIntervalMs;
24
30
  }
@@ -61,33 +67,51 @@ class CVRPurger {
61
67
  purgeInactiveCVRs(maxCVRs) {
62
68
  return this.#db.begin(READ_COMMITTED, async (sql) => {
63
69
  disableStatementTimeout(sql);
64
- const threshold = Date.now() - this.#inactivityThresholdMs;
70
+ const now = Date.now();
71
+ const threshold = now - this.#inactivityThresholdMs;
72
+ const tombstonePurgeThreshold = now - this.#tombstonePurgeThresholdMs;
65
73
  const ids = (await sql`
66
74
  SELECT "clientGroupID" FROM ${sql(this.#schema)}.instances
67
- WHERE "lastActive" < ${threshold}
75
+ WHERE NOT "deleted" AND "lastActive" < ${threshold}
68
76
  ORDER BY "lastActive" ASC
69
77
  LIMIT ${maxCVRs}
70
78
  FOR UPDATE SKIP LOCKED
71
79
  `.values()).flat();
72
80
  if (ids.length > 0) {
73
- await Promise.all(
74
- [
75
- "desires",
76
- "queries",
77
- "clients",
78
- "instances",
79
- "rows",
80
- "rowsVersion"
81
- ].map(
82
- (table) => sql`
81
+ const stmts = [
82
+ "desires",
83
+ "queries",
84
+ "clients",
85
+ "rows",
86
+ "rowsVersion"
87
+ ].map(
88
+ (table) => sql`
83
89
  DELETE FROM ${sql(this.#schema)}.${sql(table)}
84
90
  WHERE "clientGroupID" IN ${sql(ids)}`.execute()
85
- )
86
91
  );
92
+ stmts.push(
93
+ sql`
94
+ UPDATE ${sql(this.#schema)}.instances
95
+ SET "deleted" = TRUE,
96
+ "version" = '00',
97
+ "ttlClock" = 0,
98
+ "replicaVersion" = NULL,
99
+ "owner" = NULL,
100
+ "grantedAt" = NULL,
101
+ "clientSchema" = NULL
102
+ WHERE "clientGroupID" IN ${sql(ids)}`.execute()
103
+ );
104
+ stmts.push(
105
+ sql`
106
+ DELETE FROM ${sql(this.#schema)}.instances
107
+ WHERE "deleted" AND "lastActive" < ${tombstonePurgeThreshold}
108
+ `.execute()
109
+ );
110
+ await Promise.all(stmts);
87
111
  }
88
112
  const [{ remaining }] = await sql`
89
113
  SELECT COUNT(*) AS remaining FROM ${sql(this.#schema)}.instances
90
- WHERE "lastActive" < ${threshold}
114
+ WHERE NOT "deleted" AND "lastActive" < ${threshold}
91
115
  `;
92
116
  return { purged: ids.length, remaining: Number(remaining) };
93
117
  });
@@ -1 +1 @@
1
- {"version":3,"file":"cvr-purger.js","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-purger.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {READ_COMMITTED} from '../../db/mode-enum.ts';\nimport {disableStatementTimeout, type PostgresDB} from '../../types/pg.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {Service} from '../service.ts';\n\nconst MINUTE = 60 * 1000;\nconst MAX_PURGE_INTERVAL_MS = 16 * MINUTE;\n\ntype Options = {\n inactivityThresholdMs: number;\n initialBatchSize: number;\n initialIntervalMs: number;\n};\n\nexport class CVRPurger implements Service {\n readonly id = 'reaper';\n\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #inactivityThresholdMs: number;\n readonly #initialBatchSize: number;\n readonly #initialIntervalMs: number;\n readonly #state = new RunningState('reaper');\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n {inactivityThresholdMs, initialBatchSize, initialIntervalMs}: Options,\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#inactivityThresholdMs = inactivityThresholdMs;\n this.#initialBatchSize = initialBatchSize;\n this.#initialIntervalMs = initialIntervalMs;\n }\n\n async run() {\n let purgeable: number | undefined;\n let maxCVRsPerPurge = this.#initialBatchSize;\n let purgeInterval = this.#initialIntervalMs;\n\n if (this.#initialBatchSize === 0) {\n this.#lc.warn?.(\n `CVR garbage collection is disabled (initialBatchSize = 0)`,\n );\n // Do nothing and just wait to be stopped.\n await this.#state.stopped();\n } else {\n this.#lc.info?.(\n `running cvr-purger with`,\n await this.#db`SHOW statement_timeout`,\n );\n }\n\n while (this.#state.shouldRun()) {\n try {\n const start = performance.now();\n const {purged, remaining} =\n await this.purgeInactiveCVRs(maxCVRsPerPurge);\n\n if (purgeable !== undefined && remaining > purgeable) {\n // If the number of purgeable CVRs has grown even after the purge,\n // increase the number purged per round to achieve a steady state.\n maxCVRsPerPurge += this.#initialBatchSize;\n this.#lc.info?.(`increased CVRs per purge to ${maxCVRsPerPurge}`);\n }\n purgeable = remaining;\n\n purgeInterval =\n purgeable > 0\n ? this.#initialIntervalMs\n : Math.min(purgeInterval * 2, MAX_PURGE_INTERVAL_MS);\n const elapsed = performance.now() - start;\n this.#lc.info?.(\n `purged ${purged} inactive CVRs (${elapsed.toFixed(2)} ms). Next purge in ${purgeInterval} ms`,\n );\n await this.#state.sleep(purgeInterval);\n } catch (e) {\n this.#lc.warn?.(`error encountered while garbage collecting CVRs`, e);\n }\n }\n }\n\n // Exported for testing.\n purgeInactiveCVRs(\n maxCVRs: number,\n ): Promise<{purged: number; remaining: number}> {\n return this.#db.begin(READ_COMMITTED, async sql => {\n disableStatementTimeout(sql);\n\n const threshold = Date.now() - this.#inactivityThresholdMs;\n // Implementation note: `FOR UPDATE` will prevent a syncer from\n // concurrently updating the CVR, since the update also performs\n // a `SELECT ... FOR UPDATE`, instead causing that update to\n // fail, which will cause the client to create a new CVR.\n //\n // `SKIP LOCKED` will skip over CVRs that a syncer is already\n // in the process of updating. In this manner, an in-progress\n // update effectively excludes the CVR from the purge.\n const ids = (\n await sql<{clientGroupID: string}[]>`\n SELECT \"clientGroupID\" FROM ${sql(this.#schema)}.instances\n WHERE \"lastActive\" < ${threshold}\n ORDER BY \"lastActive\" ASC\n LIMIT ${maxCVRs}\n FOR UPDATE SKIP LOCKED\n `.values()\n ).flat();\n\n if (ids.length > 0) {\n // Explicitly delete rows from cvr tables from \"bottom\" up. Even\n // though all tables eventually reference a (\"top\") ancestor row in the\n // \"instances\" or \"rowsVersion\" tables, relying on foreign key\n // cascading deletes can be suboptimal when the foreign key is not a\n // prefix of the primary key (e.g. the \"desires\" foreign key reference\n // to the \"queries\" table is not a prefix of the \"desires\" primary\n // key).\n await Promise.all(\n [\n 'desires',\n 'queries',\n 'clients',\n 'instances',\n 'rows',\n 'rowsVersion',\n ].map(table =>\n sql`\n DELETE FROM ${sql(this.#schema)}.${sql(table)} \n WHERE \"clientGroupID\" IN ${sql(ids)}`.execute(),\n ),\n );\n }\n\n const [{remaining}] = await sql<[{remaining: bigint}]>`\n SELECT COUNT(*) AS remaining FROM ${sql(this.#schema)}.instances\n WHERE \"lastActive\" < ${threshold}\n `;\n\n return {purged: ids.length, remaining: Number(remaining)};\n });\n }\n\n stop(): Promise<void> {\n this.#state.stop(this.#lc);\n return promiseVoid;\n }\n}\n"],"names":[],"mappings":";;;;;AAQA,MAAM,SAAS,KAAK;AACpB,MAAM,wBAAwB,KAAK;AAQ5B,MAAM,UAA6B;AAAA,EAC/B,KAAK;AAAA,EAEL;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS,IAAI,aAAa,QAAQ;AAAA,EAE3C,YACE,IACA,IACA,OACA,EAAC,uBAAuB,kBAAkB,qBAC1C;AACA,SAAK,MAAM;AACX,SAAK,MAAM;AACX,SAAK,UAAU,UAAU,KAAK;AAC9B,SAAK,yBAAyB;AAC9B,SAAK,oBAAoB;AACzB,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEA,MAAM,MAAM;AACV,QAAI;AACJ,QAAI,kBAAkB,KAAK;AAC3B,QAAI,gBAAgB,KAAK;AAEzB,QAAI,KAAK,sBAAsB,GAAG;AAChC,WAAK,IAAI;AAAA,QACP;AAAA,MAAA;AAGF,YAAM,KAAK,OAAO,QAAA;AAAA,IACpB,OAAO;AACL,WAAK,IAAI;AAAA,QACP;AAAA,QACA,MAAM,KAAK;AAAA,MAAA;AAAA,IAEf;AAEA,WAAO,KAAK,OAAO,aAAa;AAC9B,UAAI;AACF,cAAM,QAAQ,YAAY,IAAA;AAC1B,cAAM,EAAC,QAAQ,UAAA,IACb,MAAM,KAAK,kBAAkB,eAAe;AAE9C,YAAI,cAAc,UAAa,YAAY,WAAW;AAGpD,6BAAmB,KAAK;AACxB,eAAK,IAAI,OAAO,+BAA+B,eAAe,EAAE;AAAA,QAClE;AACA,oBAAY;AAEZ,wBACE,YAAY,IACR,KAAK,qBACL,KAAK,IAAI,gBAAgB,GAAG,qBAAqB;AACvD,cAAM,UAAU,YAAY,IAAA,IAAQ;AACpC,aAAK,IAAI;AAAA,UACP,UAAU,MAAM,mBAAmB,QAAQ,QAAQ,CAAC,CAAC,uBAAuB,aAAa;AAAA,QAAA;AAE3F,cAAM,KAAK,OAAO,MAAM,aAAa;AAAA,MACvC,SAAS,GAAG;AACV,aAAK,IAAI,OAAO,mDAAmD,CAAC;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,kBACE,SAC8C;AAC9C,WAAO,KAAK,IAAI,MAAM,gBAAgB,OAAM,QAAO;AACjD,8BAAwB,GAAG;AAE3B,YAAM,YAAY,KAAK,IAAA,IAAQ,KAAK;AASpC,YAAM,OACJ,MAAM;AAAA,wCAC0B,IAAI,KAAK,OAAO,CAAC;AAAA,mCACtB,SAAS;AAAA;AAAA,oBAExB,OAAO;AAAA;AAAA,QAEnB,OAAA,GACA,KAAA;AAEF,UAAI,IAAI,SAAS,GAAG;AAQlB,cAAM,QAAQ;AAAA,UACZ;AAAA,YACE;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UAAA,EACA;AAAA,YAAI,CAAA,UACJ;AAAA,0BACc,IAAI,KAAK,OAAO,CAAC,IAAI,IAAI,KAAK,CAAC;AAAA,yCAChB,IAAI,GAAG,CAAC,GAAG,QAAA;AAAA,UAAQ;AAAA,QAClD;AAAA,MAEJ;AAEA,YAAM,CAAC,EAAC,WAAU,IAAI,MAAM;AAAA,4CACU,IAAI,KAAK,OAAO,CAAC;AAAA,iCAC5B,SAAS;AAAA;AAGpC,aAAO,EAAC,QAAQ,IAAI,QAAQ,WAAW,OAAO,SAAS,EAAA;AAAA,IACzD,CAAC;AAAA,EACH;AAAA,EAEA,OAAsB;AACpB,SAAK,OAAO,KAAK,KAAK,GAAG;AACzB,WAAO;AAAA,EACT;AACF;"}
1
+ {"version":3,"file":"cvr-purger.js","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-purger.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {promiseVoid} from '../../../../shared/src/resolved-promises.ts';\nimport {READ_COMMITTED} from '../../db/mode-enum.ts';\nimport {disableStatementTimeout, type PostgresDB} from '../../types/pg.ts';\nimport {cvrSchema, type ShardID} from '../../types/shards.ts';\nimport {RunningState} from '../running-state.ts';\nimport type {Service} from '../service.ts';\n\nconst MINUTE = 60 * 1000;\nconst MAX_PURGE_INTERVAL_MS = 16 * MINUTE;\n\n// Purge tombstones after 31 days to facilitate up to a 30-day actives metric.\nconst TOMBSTONE_PURGE_THRESHOLD = 31 * 24 * 60 * 60 * 1000;\n\ntype Options = {\n inactivityThresholdMs: number;\n initialBatchSize: number;\n initialIntervalMs: number;\n};\n\nexport class CVRPurger implements Service {\n readonly id = 'reaper';\n\n readonly #lc: LogContext;\n readonly #db: PostgresDB;\n readonly #schema: string;\n readonly #inactivityThresholdMs: number;\n readonly #tombstonePurgeThresholdMs: number;\n readonly #initialBatchSize: number;\n readonly #initialIntervalMs: number;\n readonly #state = new RunningState('reaper');\n\n constructor(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardID,\n {inactivityThresholdMs, initialBatchSize, initialIntervalMs}: Options,\n tombstonePurgeThreshold = TOMBSTONE_PURGE_THRESHOLD,\n ) {\n this.#lc = lc;\n this.#db = db;\n this.#schema = cvrSchema(shard);\n this.#inactivityThresholdMs = inactivityThresholdMs;\n this.#tombstonePurgeThresholdMs = Math.max(\n tombstonePurgeThreshold,\n inactivityThresholdMs,\n );\n this.#initialBatchSize = initialBatchSize;\n this.#initialIntervalMs = initialIntervalMs;\n }\n\n async run() {\n let purgeable: number | undefined;\n let maxCVRsPerPurge = this.#initialBatchSize;\n let purgeInterval = this.#initialIntervalMs;\n\n if (this.#initialBatchSize === 0) {\n this.#lc.warn?.(\n `CVR garbage collection is disabled (initialBatchSize = 0)`,\n );\n // Do nothing and just wait to be stopped.\n await this.#state.stopped();\n } else {\n this.#lc.info?.(\n `running cvr-purger with`,\n await this.#db`SHOW statement_timeout`,\n );\n }\n\n while (this.#state.shouldRun()) {\n try {\n const start = performance.now();\n const {purged, remaining} =\n await this.purgeInactiveCVRs(maxCVRsPerPurge);\n\n if (purgeable !== undefined && remaining > purgeable) {\n // If the number of purgeable CVRs has grown even after the purge,\n // increase the number purged per round to achieve a steady state.\n maxCVRsPerPurge += this.#initialBatchSize;\n this.#lc.info?.(`increased CVRs per purge to ${maxCVRsPerPurge}`);\n }\n purgeable = remaining;\n\n purgeInterval =\n purgeable > 0\n ? this.#initialIntervalMs\n : Math.min(purgeInterval * 2, MAX_PURGE_INTERVAL_MS);\n const elapsed = performance.now() - start;\n this.#lc.info?.(\n `purged ${purged} inactive CVRs (${elapsed.toFixed(2)} ms). Next purge in ${purgeInterval} ms`,\n );\n await this.#state.sleep(purgeInterval);\n } catch (e) {\n this.#lc.warn?.(`error encountered while garbage collecting CVRs`, e);\n }\n }\n }\n\n // Exported for testing.\n purgeInactiveCVRs(\n maxCVRs: number,\n ): Promise<{purged: number; remaining: number}> {\n return this.#db.begin(READ_COMMITTED, async sql => {\n disableStatementTimeout(sql);\n\n const now = Date.now();\n const threshold = now - this.#inactivityThresholdMs;\n const tombstonePurgeThreshold = now - this.#tombstonePurgeThresholdMs;\n // Implementation note: `FOR UPDATE` will prevent a syncer from\n // concurrently updating the CVR, since the update also performs\n // a `SELECT ... FOR UPDATE`, instead causing that update to\n // fail, which will cause the client to create a new CVR.\n //\n // `SKIP LOCKED` will skip over CVRs that a syncer is already\n // in the process of updating. In this manner, an in-progress\n // update effectively excludes the CVR from the purge.\n const ids = (\n await sql<{clientGroupID: string}[]>`\n SELECT \"clientGroupID\" FROM ${sql(this.#schema)}.instances\n WHERE NOT \"deleted\" AND \"lastActive\" < ${threshold}\n ORDER BY \"lastActive\" ASC\n LIMIT ${maxCVRs}\n FOR UPDATE SKIP LOCKED\n `.values()\n ).flat();\n\n if (ids.length > 0) {\n // Explicitly delete rows from cvr tables from \"bottom\" up. Relying on\n // foreign key cascading deletes can be suboptimal when the foreign key\n // is not a prefix of the primary key (e.g. the \"desires\" foreign key\n // reference to the \"queries\" table is not a prefix of the \"desires\"\n // primary key).\n const stmts = [\n 'desires',\n 'queries',\n 'clients',\n 'rows',\n 'rowsVersion',\n ].map(table =>\n sql`\n DELETE FROM ${sql(this.#schema)}.${sql(table)} \n WHERE \"clientGroupID\" IN ${sql(ids)}`.execute(),\n );\n // Tombstones are written for the `instances` rows, preserving the\n // \"profileID\" and \"lastActive\" columns for computing usage stats.\n //\n // For backwards compatibility (i.e. older zero-caches that do not\n // check the \"deleted\" column) reset the \"version\" to '00' to trigger\n // the ClientNotFound error via\n // view-syncer.ts:checkClientAndCVRVersions()\n stmts.push(\n sql`\n UPDATE ${sql(this.#schema)}.instances\n SET \"deleted\" = TRUE, \n \"version\" = '00', \n \"ttlClock\" = 0,\n \"replicaVersion\" = NULL, \n \"owner\" = NULL,\n \"grantedAt\" = NULL,\n \"clientSchema\" = NULL\n WHERE \"clientGroupID\" IN ${sql(ids)}`.execute(),\n );\n // Tombstone rows are deleted after the tombstonePurgeThreshold.\n stmts.push(\n sql`\n DELETE FROM ${sql(this.#schema)}.instances\n WHERE \"deleted\" AND \"lastActive\" < ${tombstonePurgeThreshold}\n `.execute(),\n );\n await Promise.all(stmts);\n }\n\n const [{remaining}] = await sql<[{remaining: bigint}]>`\n SELECT COUNT(*) AS remaining FROM ${sql(this.#schema)}.instances\n WHERE NOT \"deleted\" AND \"lastActive\" < ${threshold}\n `;\n\n return {purged: ids.length, remaining: Number(remaining)};\n });\n }\n\n stop(): Promise<void> {\n this.#state.stop(this.#lc);\n return promiseVoid;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -52,7 +52,7 @@ export declare class CVRStore {
52
52
  * `undefined`.
53
53
  */
54
54
  getTTLClock(): Promise<TTLClock | undefined>;
55
- putInstance({ version, replicaVersion, lastActive, clientSchema, ttlClock, }: Pick<CVRSnapshot, 'version' | 'replicaVersion' | 'lastActive' | 'clientSchema' | 'ttlClock'>): void;
55
+ putInstance({ version, replicaVersion, lastActive, clientSchema, profileID, ttlClock, }: Pick<CVRSnapshot, 'version' | 'replicaVersion' | 'lastActive' | 'clientSchema' | 'profileID' | 'ttlClock'>): void;
56
56
  markQueryAsDeleted(version: CVRVersion, queryPatch: QueryPatch): void;
57
57
  putQuery(query: QueryRecord): void;
58
58
  updateQuery(query: QueryRecord): void;
@@ -78,6 +78,9 @@ export declare class CVRStore {
78
78
  * (i.e. by doing a plain `SELECT` rather than a `SELECT ... FOR UPDATE`).
79
79
  */
80
80
  export declare function checkVersion(tx: PostgresTransaction, schema: string, clientGroupID: string, expectedCurrentVersion: CVRVersion): Promise<void>;
81
+ export declare class ClientNotFoundError extends ProtocolErrorWithLevel {
82
+ constructor(message: string);
83
+ }
81
84
  export declare class ConcurrentModificationException extends ProtocolErrorWithLevel {
82
85
  readonly name = "ConcurrentModificationException";
83
86
  constructor(expectedVersion: string, actualVersion: string);
@@ -1 +1 @@
1
- {"version":3,"file":"cvr-store.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cvr-store.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/view-syncer/cvr-store.ts"],"names":[],"mappings":"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"}
@@ -118,11 +118,9 @@ class CVRStore {
118
118
  return result;
119
119
  }
120
120
  assert(err);
121
- throw new ProtocolErrorWithLevel({
122
- kind: ClientNotFound,
123
- message: `max attempts exceeded waiting for CVR@${err.cvrVersion} to catch up from ${err.rowsVersion}`,
124
- origin: ZeroCache
125
- });
121
+ throw new ClientNotFoundError(
122
+ `max attempts exceeded waiting for CVR@${err.cvrVersion} to catch up from ${err.rowsVersion}`
123
+ );
126
124
  });
127
125
  }
128
126
  async #load(lc, lastConnectTime) {
@@ -137,7 +135,8 @@ class CVRStore {
137
135
  replicaVersion: null,
138
136
  clients: {},
139
137
  queries: {},
140
- clientSchema: null
138
+ clientSchema: null,
139
+ profileID: null
141
140
  };
142
141
  const [instance, clientsRows, queryRows, desiresRows] = await this.#db.begin(READONLY, (tx) => [
143
142
  tx`SELECT cvr."version",
@@ -147,6 +146,8 @@ class CVRStore {
147
146
  "owner",
148
147
  "grantedAt",
149
148
  "clientSchema",
149
+ "profileID",
150
+ "deleted",
150
151
  rows."version" as "rowsVersion"
151
152
  FROM ${this.#cvr("instances")} AS cvr
152
153
  LEFT JOIN ${this.#cvr("rowsVersion")} AS rows
@@ -176,7 +177,8 @@ class CVRStore {
176
177
  ttlClock: ttlClockFromNumber(0),
177
178
  // TTL clock starts at 0 for new instances
178
179
  replicaVersion: null,
179
- clientSchema: null
180
+ clientSchema: null,
181
+ profileID: null
180
182
  });
181
183
  } else {
182
184
  assert(instance.length === 1);
@@ -188,8 +190,15 @@ class CVRStore {
188
190
  owner,
189
191
  grantedAt,
190
192
  rowsVersion,
191
- clientSchema
193
+ clientSchema,
194
+ profileID,
195
+ deleted
192
196
  } = instance[0];
197
+ if (deleted) {
198
+ throw new ClientNotFoundError(
199
+ "Client has been purged due to inactivity"
200
+ );
201
+ }
193
202
  if (owner !== this.#taskID) {
194
203
  if ((grantedAt ?? 0) > lastConnectTime) {
195
204
  throw new OwnershipError(owner, grantedAt, lastConnectTime);
@@ -211,6 +220,7 @@ class CVRStore {
211
220
  cvr.lastActive = lastActive;
212
221
  cvr.ttlClock = ttlClock;
213
222
  cvr.replicaVersion = replicaVersion;
223
+ cvr.profileID = profileID;
214
224
  try {
215
225
  cvr.clientSchema = clientSchema === null ? null : parse(clientSchema, clientSchemaSchema);
216
226
  } catch (e) {
@@ -309,6 +319,7 @@ class CVRStore {
309
319
  replicaVersion,
310
320
  lastActive,
311
321
  clientSchema,
322
+ profileID,
312
323
  ttlClock
313
324
  }) {
314
325
  this.#writes.add({
@@ -322,7 +333,8 @@ class CVRStore {
322
333
  replicaVersion,
323
334
  owner: this.#taskID,
324
335
  grantedAt: lastConnectTime,
325
- clientSchema
336
+ clientSchema,
337
+ profileID
326
338
  };
327
339
  return tx`
328
340
  INSERT INTO ${this.#cvr("instances")} ${tx(change)}
@@ -697,6 +709,15 @@ async function checkVersion(tx, schema, clientGroupID, expectedCurrentVersion) {
697
709
  throw new ConcurrentModificationException(expected, version2);
698
710
  }
699
711
  }
712
+ class ClientNotFoundError extends ProtocolErrorWithLevel {
713
+ constructor(message) {
714
+ super({
715
+ kind: ClientNotFound,
716
+ message,
717
+ origin: ZeroCache
718
+ });
719
+ }
720
+ }
700
721
  class ConcurrentModificationException extends ProtocolErrorWithLevel {
701
722
  name = "ConcurrentModificationException";
702
723
  constructor(expectedVersion, actualVersion) {
@@ -750,6 +771,7 @@ class RowsVersionBehindError extends Error {
750
771
  }
751
772
  export {
752
773
  CVRStore,
774
+ ClientNotFoundError,
753
775
  ConcurrentModificationException,
754
776
  InvalidClientSchemaError,
755
777
  OwnershipError,