clawsql 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/.env.example +97 -0
  2. package/README.md +372 -0
  3. package/dist/__tests__/config/settings.test.d.ts +5 -0
  4. package/dist/__tests__/config/settings.test.d.ts.map +1 -0
  5. package/dist/__tests__/config/settings.test.js +154 -0
  6. package/dist/__tests__/config/settings.test.js.map +1 -0
  7. package/dist/__tests__/core/discovery/topology.test.d.ts +5 -0
  8. package/dist/__tests__/core/discovery/topology.test.d.ts.map +1 -0
  9. package/dist/__tests__/core/discovery/topology.test.js +191 -0
  10. package/dist/__tests__/core/discovery/topology.test.js.map +1 -0
  11. package/dist/__tests__/core/failover/executor.test.d.ts +5 -0
  12. package/dist/__tests__/core/failover/executor.test.d.ts.map +1 -0
  13. package/dist/__tests__/core/failover/executor.test.js +256 -0
  14. package/dist/__tests__/core/failover/executor.test.js.map +1 -0
  15. package/dist/__tests__/core/monitoring/collector.test.d.ts +5 -0
  16. package/dist/__tests__/core/monitoring/collector.test.d.ts.map +1 -0
  17. package/dist/__tests__/core/monitoring/collector.test.js +131 -0
  18. package/dist/__tests__/core/monitoring/collector.test.js.map +1 -0
  19. package/dist/__tests__/core/monitoring/exporters.test.d.ts +5 -0
  20. package/dist/__tests__/core/monitoring/exporters.test.d.ts.map +1 -0
  21. package/dist/__tests__/core/monitoring/exporters.test.js +90 -0
  22. package/dist/__tests__/core/monitoring/exporters.test.js.map +1 -0
  23. package/dist/__tests__/core/routing/proxysql-manager.test.d.ts +5 -0
  24. package/dist/__tests__/core/routing/proxysql-manager.test.d.ts.map +1 -0
  25. package/dist/__tests__/core/routing/proxysql-manager.test.js +155 -0
  26. package/dist/__tests__/core/routing/proxysql-manager.test.js.map +1 -0
  27. package/dist/__tests__/types/index.test.d.ts +5 -0
  28. package/dist/__tests__/types/index.test.d.ts.map +1 -0
  29. package/dist/__tests__/types/index.test.js +290 -0
  30. package/dist/__tests__/types/index.test.js.map +1 -0
  31. package/dist/__tests__/utils/exceptions.test.d.ts +5 -0
  32. package/dist/__tests__/utils/exceptions.test.d.ts.map +1 -0
  33. package/dist/__tests__/utils/exceptions.test.js +142 -0
  34. package/dist/__tests__/utils/exceptions.test.js.map +1 -0
  35. package/dist/api/routes/clusters.d.ts +7 -0
  36. package/dist/api/routes/clusters.d.ts.map +1 -0
  37. package/dist/api/routes/clusters.js +123 -0
  38. package/dist/api/routes/clusters.js.map +1 -0
  39. package/dist/api/routes/config.d.ts +7 -0
  40. package/dist/api/routes/config.d.ts.map +1 -0
  41. package/dist/api/routes/config.js +65 -0
  42. package/dist/api/routes/config.js.map +1 -0
  43. package/dist/api/routes/failover.d.ts +7 -0
  44. package/dist/api/routes/failover.d.ts.map +1 -0
  45. package/dist/api/routes/failover.js +100 -0
  46. package/dist/api/routes/failover.js.map +1 -0
  47. package/dist/api/routes/instances.d.ts +11 -0
  48. package/dist/api/routes/instances.d.ts.map +1 -0
  49. package/dist/api/routes/instances.js +315 -0
  50. package/dist/api/routes/instances.js.map +1 -0
  51. package/dist/api/routes/monitoring.d.ts +7 -0
  52. package/dist/api/routes/monitoring.d.ts.map +1 -0
  53. package/dist/api/routes/monitoring.js +72 -0
  54. package/dist/api/routes/monitoring.js.map +1 -0
  55. package/dist/api/routes/webhooks.d.ts +12 -0
  56. package/dist/api/routes/webhooks.d.ts.map +1 -0
  57. package/dist/api/routes/webhooks.js +232 -0
  58. package/dist/api/routes/webhooks.js.map +1 -0
  59. package/dist/api/schemas/index.d.ts +965 -0
  60. package/dist/api/schemas/index.d.ts.map +1 -0
  61. package/dist/api/schemas/index.js +171 -0
  62. package/dist/api/schemas/index.js.map +1 -0
  63. package/dist/app.d.ts +13 -0
  64. package/dist/app.d.ts.map +1 -0
  65. package/dist/app.js +197 -0
  66. package/dist/app.js.map +1 -0
  67. package/dist/bin/clawsql.d.ts +12 -0
  68. package/dist/bin/clawsql.d.ts.map +1 -0
  69. package/dist/bin/clawsql.js +43 -0
  70. package/dist/bin/clawsql.js.map +1 -0
  71. package/dist/cli/agent/handler.d.ts +73 -0
  72. package/dist/cli/agent/handler.d.ts.map +1 -0
  73. package/dist/cli/agent/handler.js +258 -0
  74. package/dist/cli/agent/handler.js.map +1 -0
  75. package/dist/cli/agent/index.d.ts +14 -0
  76. package/dist/cli/agent/index.d.ts.map +1 -0
  77. package/dist/cli/agent/index.js +30 -0
  78. package/dist/cli/agent/index.js.map +1 -0
  79. package/dist/cli/agent/openclaw-integration.d.ts +81 -0
  80. package/dist/cli/agent/openclaw-integration.d.ts.map +1 -0
  81. package/dist/cli/agent/openclaw-integration.js +341 -0
  82. package/dist/cli/agent/openclaw-integration.js.map +1 -0
  83. package/dist/cli/agent/providers/anthropic.d.ts +27 -0
  84. package/dist/cli/agent/providers/anthropic.d.ts.map +1 -0
  85. package/dist/cli/agent/providers/anthropic.js +106 -0
  86. package/dist/cli/agent/providers/anthropic.js.map +1 -0
  87. package/dist/cli/agent/providers/base.d.ts +91 -0
  88. package/dist/cli/agent/providers/base.d.ts.map +1 -0
  89. package/dist/cli/agent/providers/base.js +24 -0
  90. package/dist/cli/agent/providers/base.js.map +1 -0
  91. package/dist/cli/agent/providers/openai.d.ts +27 -0
  92. package/dist/cli/agent/providers/openai.d.ts.map +1 -0
  93. package/dist/cli/agent/providers/openai.js +98 -0
  94. package/dist/cli/agent/providers/openai.js.map +1 -0
  95. package/dist/cli/agent/tools/index.d.ts +32 -0
  96. package/dist/cli/agent/tools/index.d.ts.map +1 -0
  97. package/dist/cli/agent/tools/index.js +263 -0
  98. package/dist/cli/agent/tools/index.js.map +1 -0
  99. package/dist/cli/commands/cleanup.d.ts +12 -0
  100. package/dist/cli/commands/cleanup.d.ts.map +1 -0
  101. package/dist/cli/commands/cleanup.js +205 -0
  102. package/dist/cli/commands/cleanup.js.map +1 -0
  103. package/dist/cli/commands/clusters.d.ts +12 -0
  104. package/dist/cli/commands/clusters.d.ts.map +1 -0
  105. package/dist/cli/commands/clusters.js +468 -0
  106. package/dist/cli/commands/clusters.js.map +1 -0
  107. package/dist/cli/commands/config.d.ts +12 -0
  108. package/dist/cli/commands/config.d.ts.map +1 -0
  109. package/dist/cli/commands/config.js +406 -0
  110. package/dist/cli/commands/config.js.map +1 -0
  111. package/dist/cli/commands/cron.d.ts +12 -0
  112. package/dist/cli/commands/cron.d.ts.map +1 -0
  113. package/dist/cli/commands/cron.js +215 -0
  114. package/dist/cli/commands/cron.js.map +1 -0
  115. package/dist/cli/commands/doctor.d.ts +13 -0
  116. package/dist/cli/commands/doctor.d.ts.map +1 -0
  117. package/dist/cli/commands/doctor.js +687 -0
  118. package/dist/cli/commands/doctor.js.map +1 -0
  119. package/dist/cli/commands/failover.d.ts +16 -0
  120. package/dist/cli/commands/failover.d.ts.map +1 -0
  121. package/dist/cli/commands/failover.js +333 -0
  122. package/dist/cli/commands/failover.js.map +1 -0
  123. package/dist/cli/commands/health.d.ts +12 -0
  124. package/dist/cli/commands/health.d.ts.map +1 -0
  125. package/dist/cli/commands/health.js +125 -0
  126. package/dist/cli/commands/health.js.map +1 -0
  127. package/dist/cli/commands/help.d.ts +12 -0
  128. package/dist/cli/commands/help.d.ts.map +1 -0
  129. package/dist/cli/commands/help.js +52 -0
  130. package/dist/cli/commands/help.js.map +1 -0
  131. package/dist/cli/commands/instances.d.ts +12 -0
  132. package/dist/cli/commands/instances.d.ts.map +1 -0
  133. package/dist/cli/commands/instances.js +801 -0
  134. package/dist/cli/commands/instances.js.map +1 -0
  135. package/dist/cli/commands/notify.d.ts +12 -0
  136. package/dist/cli/commands/notify.d.ts.map +1 -0
  137. package/dist/cli/commands/notify.js +43 -0
  138. package/dist/cli/commands/notify.js.map +1 -0
  139. package/dist/cli/commands/sql.d.ts +12 -0
  140. package/dist/cli/commands/sql.d.ts.map +1 -0
  141. package/dist/cli/commands/sql.js +90 -0
  142. package/dist/cli/commands/sql.js.map +1 -0
  143. package/dist/cli/commands/start.d.ts +12 -0
  144. package/dist/cli/commands/start.d.ts.map +1 -0
  145. package/dist/cli/commands/start.js +174 -0
  146. package/dist/cli/commands/start.js.map +1 -0
  147. package/dist/cli/commands/status.d.ts +12 -0
  148. package/dist/cli/commands/status.d.ts.map +1 -0
  149. package/dist/cli/commands/status.js +218 -0
  150. package/dist/cli/commands/status.js.map +1 -0
  151. package/dist/cli/commands/stop.d.ts +12 -0
  152. package/dist/cli/commands/stop.d.ts.map +1 -0
  153. package/dist/cli/commands/stop.js +128 -0
  154. package/dist/cli/commands/stop.js.map +1 -0
  155. package/dist/cli/commands/topology.d.ts +12 -0
  156. package/dist/cli/commands/topology.d.ts.map +1 -0
  157. package/dist/cli/commands/topology.js +106 -0
  158. package/dist/cli/commands/topology.js.map +1 -0
  159. package/dist/cli/completer.d.ts +47 -0
  160. package/dist/cli/completer.d.ts.map +1 -0
  161. package/dist/cli/completer.js +332 -0
  162. package/dist/cli/completer.js.map +1 -0
  163. package/dist/cli/formatter.d.ts +165 -0
  164. package/dist/cli/formatter.d.ts.map +1 -0
  165. package/dist/cli/formatter.js +408 -0
  166. package/dist/cli/formatter.js.map +1 -0
  167. package/dist/cli/index.d.ts +21 -0
  168. package/dist/cli/index.d.ts.map +1 -0
  169. package/dist/cli/index.js +79 -0
  170. package/dist/cli/index.js.map +1 -0
  171. package/dist/cli/raw-input.d.ts +97 -0
  172. package/dist/cli/raw-input.d.ts.map +1 -0
  173. package/dist/cli/raw-input.js +493 -0
  174. package/dist/cli/raw-input.js.map +1 -0
  175. package/dist/cli/registry.d.ts +103 -0
  176. package/dist/cli/registry.d.ts.map +1 -0
  177. package/dist/cli/registry.js +205 -0
  178. package/dist/cli/registry.js.map +1 -0
  179. package/dist/cli/repl.d.ts +83 -0
  180. package/dist/cli/repl.d.ts.map +1 -0
  181. package/dist/cli/repl.js +447 -0
  182. package/dist/cli/repl.js.map +1 -0
  183. package/dist/cli/ui/components.d.ts +144 -0
  184. package/dist/cli/ui/components.d.ts.map +1 -0
  185. package/dist/cli/ui/components.js +331 -0
  186. package/dist/cli/ui/components.js.map +1 -0
  187. package/dist/cli/ui/index.d.ts +7 -0
  188. package/dist/cli/ui/index.d.ts.map +1 -0
  189. package/dist/cli/ui/index.js +23 -0
  190. package/dist/cli/ui/index.js.map +1 -0
  191. package/dist/cli/utils/docker-files.d.ts +39 -0
  192. package/dist/cli/utils/docker-files.d.ts.map +1 -0
  193. package/dist/cli/utils/docker-files.js +223 -0
  194. package/dist/cli/utils/docker-files.js.map +1 -0
  195. package/dist/cli/utils/docker-prereq.d.ts +48 -0
  196. package/dist/cli/utils/docker-prereq.d.ts.map +1 -0
  197. package/dist/cli/utils/docker-prereq.js +203 -0
  198. package/dist/cli/utils/docker-prereq.js.map +1 -0
  199. package/dist/config/settings.d.ts +594 -0
  200. package/dist/config/settings.d.ts.map +1 -0
  201. package/dist/config/settings.js +250 -0
  202. package/dist/config/settings.js.map +1 -0
  203. package/dist/core/discovery/cluster-view.d.ts +50 -0
  204. package/dist/core/discovery/cluster-view.d.ts.map +1 -0
  205. package/dist/core/discovery/cluster-view.js +235 -0
  206. package/dist/core/discovery/cluster-view.js.map +1 -0
  207. package/dist/core/discovery/scanner.d.ts +70 -0
  208. package/dist/core/discovery/scanner.d.ts.map +1 -0
  209. package/dist/core/discovery/scanner.js +197 -0
  210. package/dist/core/discovery/scanner.js.map +1 -0
  211. package/dist/core/discovery/topology.d.ts +118 -0
  212. package/dist/core/discovery/topology.d.ts.map +1 -0
  213. package/dist/core/discovery/topology.js +550 -0
  214. package/dist/core/discovery/topology.js.map +1 -0
  215. package/dist/core/failover/candidate-selector.d.ts +46 -0
  216. package/dist/core/failover/candidate-selector.d.ts.map +1 -0
  217. package/dist/core/failover/candidate-selector.js +70 -0
  218. package/dist/core/failover/candidate-selector.js.map +1 -0
  219. package/dist/core/failover/executor.d.ts +104 -0
  220. package/dist/core/failover/executor.d.ts.map +1 -0
  221. package/dist/core/failover/executor.js +248 -0
  222. package/dist/core/failover/executor.js.map +1 -0
  223. package/dist/core/failover/operation-builder.d.ts +71 -0
  224. package/dist/core/failover/operation-builder.d.ts.map +1 -0
  225. package/dist/core/failover/operation-builder.js +157 -0
  226. package/dist/core/failover/operation-builder.js.map +1 -0
  227. package/dist/core/failover/operation-runner.d.ts +75 -0
  228. package/dist/core/failover/operation-runner.d.ts.map +1 -0
  229. package/dist/core/failover/operation-runner.js +191 -0
  230. package/dist/core/failover/operation-runner.js.map +1 -0
  231. package/dist/core/failover/promoter.d.ts +33 -0
  232. package/dist/core/failover/promoter.d.ts.map +1 -0
  233. package/dist/core/failover/promoter.js +97 -0
  234. package/dist/core/failover/promoter.js.map +1 -0
  235. package/dist/core/failover/recovery-manager.d.ts +47 -0
  236. package/dist/core/failover/recovery-manager.d.ts.map +1 -0
  237. package/dist/core/failover/recovery-manager.js +145 -0
  238. package/dist/core/failover/recovery-manager.js.map +1 -0
  239. package/dist/core/failover/types.d.ts +54 -0
  240. package/dist/core/failover/types.d.ts.map +1 -0
  241. package/dist/core/failover/types.js +8 -0
  242. package/dist/core/failover/types.js.map +1 -0
  243. package/dist/core/monitoring/collector.d.ts +25 -0
  244. package/dist/core/monitoring/collector.d.ts.map +1 -0
  245. package/dist/core/monitoring/collector.js +115 -0
  246. package/dist/core/monitoring/collector.js.map +1 -0
  247. package/dist/core/monitoring/exporters.d.ts +49 -0
  248. package/dist/core/monitoring/exporters.d.ts.map +1 -0
  249. package/dist/core/monitoring/exporters.js +126 -0
  250. package/dist/core/monitoring/exporters.js.map +1 -0
  251. package/dist/core/routing/proxysql-manager.d.ts +213 -0
  252. package/dist/core/routing/proxysql-manager.d.ts.map +1 -0
  253. package/dist/core/routing/proxysql-manager.js +632 -0
  254. package/dist/core/routing/proxysql-manager.js.map +1 -0
  255. package/dist/core/sync/replica-recovery.d.ts +40 -0
  256. package/dist/core/sync/replica-recovery.d.ts.map +1 -0
  257. package/dist/core/sync/replica-recovery.js +134 -0
  258. package/dist/core/sync/replica-recovery.js.map +1 -0
  259. package/dist/core/sync/sync-coordinator.d.ts +83 -0
  260. package/dist/core/sync/sync-coordinator.d.ts.map +1 -0
  261. package/dist/core/sync/sync-coordinator.js +254 -0
  262. package/dist/core/sync/sync-coordinator.js.map +1 -0
  263. package/dist/core/sync/topology-watcher.d.ts +76 -0
  264. package/dist/core/sync/topology-watcher.d.ts.map +1 -0
  265. package/dist/core/sync/topology-watcher.js +222 -0
  266. package/dist/core/sync/topology-watcher.js.map +1 -0
  267. package/dist/core/sync/types.d.ts +85 -0
  268. package/dist/core/sync/types.d.ts.map +1 -0
  269. package/dist/core/sync/types.js +8 -0
  270. package/dist/core/sync/types.js.map +1 -0
  271. package/dist/index.d.ts +5 -0
  272. package/dist/index.d.ts.map +1 -0
  273. package/dist/index.js +9 -0
  274. package/dist/index.js.map +1 -0
  275. package/dist/types/index.d.ts +212 -0
  276. package/dist/types/index.d.ts.map +1 -0
  277. package/dist/types/index.js +153 -0
  278. package/dist/types/index.js.map +1 -0
  279. package/dist/utils/database.d.ts +62 -0
  280. package/dist/utils/database.d.ts.map +1 -0
  281. package/dist/utils/database.js +257 -0
  282. package/dist/utils/database.js.map +1 -0
  283. package/dist/utils/exceptions.d.ts +69 -0
  284. package/dist/utils/exceptions.d.ts.map +1 -0
  285. package/dist/utils/exceptions.js +121 -0
  286. package/dist/utils/exceptions.js.map +1 -0
  287. package/dist/utils/logger.d.ts +20 -0
  288. package/dist/utils/logger.d.ts.map +1 -0
  289. package/dist/utils/logger.js +90 -0
  290. package/dist/utils/logger.js.map +1 -0
  291. package/dist/utils/mysql-client.d.ts +43 -0
  292. package/dist/utils/mysql-client.d.ts.map +1 -0
  293. package/dist/utils/mysql-client.js +125 -0
  294. package/dist/utils/mysql-client.js.map +1 -0
  295. package/docker/Dockerfile +61 -0
  296. package/docker/Dockerfile.node +41 -0
  297. package/docker/grafana/dashboards/clawsql.json +212 -0
  298. package/docker/grafana/provisioning/dashboards/dashboards.yml +13 -0
  299. package/docker/grafana/provisioning/datasources/datasources.yml +12 -0
  300. package/docker/init/primary.sql +26 -0
  301. package/docker/init/replica.sql +16 -0
  302. package/docker/orchestrator/orchestrator.conf.json +98 -0
  303. package/docker/prometheus/prometheus.yml +45 -0
  304. package/docker/proxysql/entrypoint.sh +8 -0
  305. package/docker/proxysql/init.sql.demo +30 -0
  306. package/docker/proxysql/proxysql.cnf +38 -0
  307. package/docker-compose.demo.yml +115 -0
  308. package/docker-compose.yml +217 -0
  309. package/init/primary.sql +19 -0
  310. package/init/replica.sql +13 -0
  311. package/package.json +84 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * ClawSQL - Replica Recovery
3
+ *
4
+ * Handles automatic recovery of replica instances after failover.
5
+ * - Starts replication on demoted primary
6
+ * - Ends maintenance mode when appropriate
7
+ */
8
+ import { MySQLInstance } from '../../types/index.js';
9
+ import { OrchestratorClient } from '../discovery/topology.js';
10
+ /**
11
+ * Result of a replica recovery attempt
12
+ */
13
+ export interface RecoveryResult {
14
+ recovered: boolean;
15
+ reason: string;
16
+ }
17
+ /**
18
+ * Attempt to recover a replica instance
19
+ *
20
+ * This is called after failover/switchover when a replica is in maintenance
21
+ * or unhealthy state. It tries to:
22
+ * 1. Start replication on the replica
23
+ * 2. End maintenance mode if the replica is healthy
24
+ *
25
+ * @param replica The replica instance to recover
26
+ * @param newPrimary The new primary to follow
27
+ * @param orchestrator Orchestrator client for API calls
28
+ * @returns Recovery result indicating success or failure with reason
29
+ */
30
+ export declare function recoverReplica(replica: MySQLInstance, newPrimary: MySQLInstance, orchestrator: OrchestratorClient): Promise<RecoveryResult>;
31
+ /**
32
+ * Recover multiple replicas in parallel
33
+ *
34
+ * @param replicas List of replicas to attempt recovery
35
+ * @param newPrimary The new primary to follow
36
+ * @param orchestrator Orchestrator client
37
+ * @returns Map of instance ID to recovery result
38
+ */
39
+ export declare function recoverReplicas(replicas: MySQLInstance[], newPrimary: MySQLInstance, orchestrator: OrchestratorClient): Promise<Map<string, RecoveryResult>>;
40
+ //# sourceMappingURL=replica-recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replica-recovery.d.ts","sourceRoot":"","sources":["../../../src/core/sync/replica-recovery.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,aAAa,EAAiB,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAK9D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,aAAa,EACtB,UAAU,EAAE,aAAa,EACzB,YAAY,EAAE,kBAAkB,GAC/B,OAAO,CAAC,cAAc,CAAC,CAoGzB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,aAAa,EAAE,EACzB,UAAU,EAAE,aAAa,EACzB,YAAY,EAAE,kBAAkB,GAC/B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CA+BtC"}
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ /**
3
+ * ClawSQL - Replica Recovery
4
+ *
5
+ * Handles automatic recovery of replica instances after failover.
6
+ * - Starts replication on demoted primary
7
+ * - Ends maintenance mode when appropriate
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.recoverReplica = recoverReplica;
11
+ exports.recoverReplicas = recoverReplicas;
12
+ const logger_js_1 = require("../../utils/logger.js");
13
+ const index_js_1 = require("../../types/index.js");
14
+ const mysql_client_js_1 = require("../../utils/mysql-client.js");
15
+ const logger = (0, logger_js_1.getLogger)('sync');
16
+ /**
17
+ * Attempt to recover a replica instance
18
+ *
19
+ * This is called after failover/switchover when a replica is in maintenance
20
+ * or unhealthy state. It tries to:
21
+ * 1. Start replication on the replica
22
+ * 2. End maintenance mode if the replica is healthy
23
+ *
24
+ * @param replica The replica instance to recover
25
+ * @param newPrimary The new primary to follow
26
+ * @param orchestrator Orchestrator client for API calls
27
+ * @returns Recovery result indicating success or failure with reason
28
+ */
29
+ async function recoverReplica(replica, newPrimary, orchestrator) {
30
+ const instanceId = `${replica.host}:${replica.port}`;
31
+ // Skip if replica is offline - can't recover
32
+ if (replica.state === index_js_1.InstanceState.OFFLINE) {
33
+ return {
34
+ recovered: false,
35
+ reason: `Instance ${instanceId} is offline, cannot recover`,
36
+ };
37
+ }
38
+ // Check if replica is in maintenance state
39
+ const inMaintenance = replica.state === index_js_1.InstanceState.MAINTENANCE;
40
+ logger.info({ instanceId, state: replica.state, inMaintenance }, 'Attempting replica recovery');
41
+ try {
42
+ // Step 1: Check if MySQL is actually reachable
43
+ const mysqlClient = (0, mysql_client_js_1.getMySQLClient)();
44
+ const replicationStatus = await mysqlClient.getReplicationStatus(replica.host, replica.port);
45
+ // If we can get replication status, MySQL is reachable
46
+ const isReachable = replicationStatus !== null;
47
+ if (!isReachable) {
48
+ // MySQL might be reachable but not configured as replica
49
+ // Try to check connectivity differently
50
+ logger.debug({ instanceId }, 'Could not get replication status, checking connectivity');
51
+ }
52
+ // Step 2: Start replication via Orchestrator
53
+ logger.info({ instanceId }, 'Starting replication via Orchestrator');
54
+ const started = await orchestrator.startSlave(replica.host, replica.port);
55
+ if (!started) {
56
+ return {
57
+ recovered: false,
58
+ reason: `Failed to start replication on ${instanceId} via Orchestrator`,
59
+ };
60
+ }
61
+ // Wait briefly for replication to start
62
+ await new Promise(resolve => {
63
+ const timer = setTimeout(resolve, 1000);
64
+ timer.unref();
65
+ });
66
+ // Step 3: Verify replication is running
67
+ const status = await mysqlClient.getReplicationStatus(replica.host, replica.port);
68
+ if (!status) {
69
+ return {
70
+ recovered: false,
71
+ reason: `Could not verify replication status on ${instanceId}`,
72
+ };
73
+ }
74
+ if (!status.ioRunning || !status.sqlRunning) {
75
+ const ioStatus = status.ioRunning ? 'running' : 'stopped';
76
+ const sqlStatus = status.sqlRunning ? 'running' : 'stopped';
77
+ return {
78
+ recovered: false,
79
+ reason: `Replication not fully running on ${instanceId}: IO=${ioStatus}, SQL=${sqlStatus}`,
80
+ };
81
+ }
82
+ // Step 4: End maintenance mode if needed
83
+ if (inMaintenance) {
84
+ logger.info({ instanceId }, 'Ending maintenance mode via Orchestrator');
85
+ const endedMaintenance = await orchestrator.endMaintenance(replica.host, replica.port);
86
+ if (!endedMaintenance) {
87
+ logger.warn({ instanceId }, 'Replication started but failed to end maintenance mode');
88
+ // Still consider recovery successful if replication is running
89
+ }
90
+ }
91
+ logger.info({ instanceId, newPrimary: `${newPrimary.host}:${newPrimary.port}` }, 'Replica recovered successfully');
92
+ return {
93
+ recovered: true,
94
+ reason: `Instance ${instanceId} recovered: replication running, following ${newPrimary.host}:${newPrimary.port}`,
95
+ };
96
+ }
97
+ catch (error) {
98
+ const message = error instanceof Error ? error.message : String(error);
99
+ logger.error({ error, instanceId }, 'Replica recovery failed');
100
+ return {
101
+ recovered: false,
102
+ reason: `Recovery failed for ${instanceId}: ${message}`,
103
+ };
104
+ }
105
+ }
106
+ /**
107
+ * Recover multiple replicas in parallel
108
+ *
109
+ * @param replicas List of replicas to attempt recovery
110
+ * @param newPrimary The new primary to follow
111
+ * @param orchestrator Orchestrator client
112
+ * @returns Map of instance ID to recovery result
113
+ */
114
+ async function recoverReplicas(replicas, newPrimary, orchestrator) {
115
+ const results = new Map();
116
+ // Filter replicas that need recovery (maintenance or offline state)
117
+ const needsRecovery = replicas.filter(r => r.state === index_js_1.InstanceState.MAINTENANCE || r.state === index_js_1.InstanceState.OFFLINE);
118
+ if (needsRecovery.length === 0) {
119
+ logger.debug('No replicas need recovery');
120
+ return results;
121
+ }
122
+ logger.info({ count: needsRecovery.length, instances: needsRecovery.map(r => `${r.host}:${r.port}`) }, 'Attempting to recover replicas');
123
+ // Attempt recovery in parallel
124
+ const recoveryPromises = needsRecovery.map(async (replica) => {
125
+ const result = await recoverReplica(replica, newPrimary, orchestrator);
126
+ return { instanceId: `${replica.host}:${replica.port}`, result };
127
+ });
128
+ const recoveryResults = await Promise.all(recoveryPromises);
129
+ for (const { instanceId, result } of recoveryResults) {
130
+ results.set(instanceId, result);
131
+ }
132
+ return results;
133
+ }
134
+ //# sourceMappingURL=replica-recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replica-recovery.js","sourceRoot":"","sources":["../../../src/core/sync/replica-recovery.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AA8BH,wCAwGC;AAUD,0CAmCC;AAjLD,qDAAkD;AAClD,mDAAoE;AAEpE,iEAA6D;AAE7D,MAAM,MAAM,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,CAAC;AAUjC;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,cAAc,CAClC,OAAsB,EACtB,UAAyB,EACzB,YAAgC;IAEhC,MAAM,UAAU,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;IAErD,6CAA6C;IAC7C,IAAI,OAAO,CAAC,KAAK,KAAK,wBAAa,CAAC,OAAO,EAAE,CAAC;QAC5C,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,YAAY,UAAU,6BAA6B;SAC5D,CAAC;IACJ,CAAC;IAED,2CAA2C;IAC3C,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,KAAK,wBAAa,CAAC,WAAW,CAAC;IAElE,MAAM,CAAC,IAAI,CACT,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,aAAa,EAAE,EACnD,6BAA6B,CAC9B,CAAC;IAEF,IAAI,CAAC;QACH,+CAA+C;QAC/C,MAAM,WAAW,GAAG,IAAA,gCAAc,GAAE,CAAC;QACrC,MAAM,iBAAiB,GAAG,MAAM,WAAW,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAE7F,uDAAuD;QACvD,MAAM,WAAW,GAAG,iBAAiB,KAAK,IAAI,CAAC;QAE/C,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,yDAAyD;YACzD,wCAAwC;YACxC,MAAM,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,EAAE,yDAAyD,CAAC,CAAC;QAC1F,CAAC;QAED,6CAA6C;QAC7C,MAAM,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,EAAE,uCAAuC,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAE1E,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,kCAAkC,UAAU,mBAAmB;aACxE,CAAC;QACJ,CAAC;QAED,wCAAwC;QACxC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;YAC1B,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACxC,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,wCAAwC;QACxC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,oBAAoB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAElF,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,0CAA0C,UAAU,EAAE;aAC/D,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;YAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1D,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;YAC5D,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,oCAAoC,UAAU,QAAQ,QAAQ,SAAS,SAAS,EAAE;aAC3F,CAAC;QACJ,CAAC;QAED,yCAAyC;QACzC,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,EAAE,0CAA0C,CAAC,CAAC;YACxE,MAAM,gBAAgB,GAAG,MAAM,YAAY,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YAEvF,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,CACT,EAAE,UAAU,EAAE,EACd,wDAAwD,CACzD,CAAC;gBACF,+DAA+D;YACjE,CAAC;QACH,CAAC;QAED,MAAM,CAAC,IAAI,CACT,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,EAAE,EAAE,EACnE,gCAAgC,CACjC,CAAC;QAEF,OAAO;YACL,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,YAAY,UAAU,8CAA8C,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,EAAE;SACjH,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,yBAAyB,CAAC,CAAC;QAC/D,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,uBAAuB,UAAU,KAAK,OAAO,EAAE;SACxD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,eAAe,CACnC,QAAyB,EACzB,UAAyB,EACzB,YAAgC;IAEhC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA0B,CAAC;IAElD,oEAAoE;IACpE,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CACnC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,wBAAa,CAAC,WAAW,IAAI,CAAC,CAAC,KAAK,KAAK,wBAAa,CAAC,OAAO,CAChF,CAAC;IAEF,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC1C,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,IAAI,CACT,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EACzF,gCAAgC,CACjC,CAAC;IAEF,+BAA+B;IAC/B,MAAM,gBAAgB,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC3D,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;QACvE,OAAO,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAE5D,KAAK,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,eAAe,EAAE,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * ClawSQL - Sync Coordinator
3
+ *
4
+ * Central coordinator for ProxySQL synchronization.
5
+ * Handles deduplication, rate limiting, and idempotent operations.
6
+ */
7
+ import { MySQLCluster } from '../../types/index.js';
8
+ import { SyncResult, SyncStats } from './types.js';
9
+ /**
10
+ * Sync Coordinator
11
+ * Manages synchronization between Orchestrator topology and ProxySQL routing.
12
+ */
13
+ export declare class SyncCoordinator {
14
+ private lastSyncTime;
15
+ private lastTopologyHash;
16
+ private locks;
17
+ private pendingDebounces;
18
+ private stats;
19
+ private readonly cooldownMs;
20
+ private readonly debounceMs;
21
+ private readonly maxRetries;
22
+ private readonly enabled;
23
+ private readonly writerHostgroup;
24
+ private readonly readerHostgroup;
25
+ constructor(options?: {
26
+ cooldownMs?: number;
27
+ debounceMs?: number;
28
+ maxRetries?: number;
29
+ enabled?: boolean;
30
+ writerHostgroup?: number;
31
+ readerHostgroup?: number;
32
+ });
33
+ /**
34
+ * Sync ProxySQL with the given cluster topology
35
+ * Uses lock and debounce mechanisms to prevent concurrent syncs
36
+ */
37
+ sync(cluster: MySQLCluster, source?: 'webhook' | 'poll' | 'manual', force?: boolean): Promise<SyncResult>;
38
+ /**
39
+ * Validate cluster input
40
+ */
41
+ private validateCluster;
42
+ /**
43
+ * Execute the actual sync with lock protection
44
+ */
45
+ private executeSync;
46
+ /**
47
+ * Perform the actual sync operation
48
+ */
49
+ private doSync;
50
+ /**
51
+ * Compute a hash of the cluster topology for change detection
52
+ */
53
+ private computeTopologyHash;
54
+ /**
55
+ * Get sync statistics
56
+ */
57
+ getStats(): SyncStats;
58
+ /**
59
+ * Reset cooldown for a specific cluster
60
+ */
61
+ resetCooldown(clusterId: string): void;
62
+ /**
63
+ * Clear all cached topology hashes
64
+ */
65
+ clearCache(): void;
66
+ /**
67
+ * Get the last sync time for a cluster
68
+ */
69
+ getLastSyncTime(clusterId: string): Date | null;
70
+ /**
71
+ * Check if a cluster is within cooldown period
72
+ */
73
+ isInCooldown(clusterId: string): boolean;
74
+ }
75
+ /**
76
+ * Get the sync coordinator instance
77
+ */
78
+ export declare function getSyncCoordinator(): SyncCoordinator;
79
+ /**
80
+ * Reset the sync coordinator (for testing)
81
+ */
82
+ export declare function resetSyncCoordinator(): void;
83
+ //# sourceMappingURL=sync-coordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-coordinator.d.ts","sourceRoot":"","sources":["../../../src/core/sync/sync-coordinator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAKnD;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,gBAAgB,CAA0C;IAClE,OAAO,CAAC,KAAK,CAKX;IAEF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAE7B,OAAO,CAAC,EAAE;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B;IASD;;;OAGG;IACG,IAAI,CACR,OAAO,EAAE,YAAY,EACrB,MAAM,GAAE,SAAS,GAAG,MAAM,GAAG,QAAmB,EAChD,KAAK,GAAE,OAAe,GACrB,OAAO,CAAC,UAAU,CAAC;IAsCtB;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;YACW,WAAW;IAuBzB;;OAEG;YACW,MAAM;IA6FpB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAW3B;;OAEG;IACH,QAAQ,IAAI,SAAS;IAIrB;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC;;OAEG;IACH,UAAU,IAAI,IAAI;IAMlB;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAK/C;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;CAKzC;AAKD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAWpD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ /**
3
+ * ClawSQL - Sync Coordinator
4
+ *
5
+ * Central coordinator for ProxySQL synchronization.
6
+ * Handles deduplication, rate limiting, and idempotent operations.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.SyncCoordinator = void 0;
10
+ exports.getSyncCoordinator = getSyncCoordinator;
11
+ exports.resetSyncCoordinator = resetSyncCoordinator;
12
+ const logger_js_1 = require("../../utils/logger.js");
13
+ const proxysql_manager_js_1 = require("../routing/proxysql-manager.js");
14
+ const settings_js_1 = require("../../config/settings.js");
15
+ const logger = (0, logger_js_1.getLogger)('sync');
16
+ /**
17
+ * Sync Coordinator
18
+ * Manages synchronization between Orchestrator topology and ProxySQL routing.
19
+ */
20
+ class SyncCoordinator {
21
+ lastSyncTime = new Map();
22
+ lastTopologyHash = new Map();
23
+ locks = new Map();
24
+ pendingDebounces = new Map();
25
+ stats = {
26
+ totalSyncs: 0,
27
+ successfulSyncs: 0,
28
+ failedSyncs: 0,
29
+ skippedSyncs: 0,
30
+ };
31
+ cooldownMs;
32
+ debounceMs;
33
+ maxRetries;
34
+ enabled;
35
+ writerHostgroup;
36
+ readerHostgroup;
37
+ constructor(options) {
38
+ this.cooldownMs = options?.cooldownMs ?? 5000;
39
+ this.debounceMs = options?.debounceMs ?? 1000;
40
+ this.maxRetries = options?.maxRetries ?? 2;
41
+ this.enabled = options?.enabled ?? true;
42
+ this.writerHostgroup = options?.writerHostgroup ?? 10;
43
+ this.readerHostgroup = options?.readerHostgroup ?? 20;
44
+ }
45
+ /**
46
+ * Sync ProxySQL with the given cluster topology
47
+ * Uses lock and debounce mechanisms to prevent concurrent syncs
48
+ */
49
+ async sync(cluster, source = 'manual', force = false) {
50
+ // Input validation
51
+ const validation = this.validateCluster(cluster);
52
+ if (!validation.valid) {
53
+ logger.warn({ error: validation.error }, 'Sync rejected - invalid input');
54
+ return { skipped: true, reason: 'invalid_input', error: validation.error };
55
+ }
56
+ const clusterId = cluster.clusterId;
57
+ // Clear any pending debounce timer
58
+ const pendingTimer = this.pendingDebounces.get(clusterId);
59
+ if (pendingTimer) {
60
+ clearTimeout(pendingTimer);
61
+ this.pendingDebounces.delete(clusterId);
62
+ }
63
+ // If sync is already in progress, return that promise (lock mechanism)
64
+ const existingLock = this.locks.get(clusterId);
65
+ if (existingLock) {
66
+ logger.debug({ clusterId, source }, 'Returning existing sync promise');
67
+ return existingLock;
68
+ }
69
+ // Debounce: wait briefly before executing
70
+ return new Promise((resolve) => {
71
+ const timer = setTimeout(() => {
72
+ this.pendingDebounces.delete(clusterId);
73
+ void this.executeSync(cluster, source, force).then(resolve);
74
+ }, force ? 0 : this.debounceMs); // Skip debounce if forced
75
+ // Don't keep the process alive just for this timer
76
+ timer.unref();
77
+ this.pendingDebounces.set(clusterId, timer);
78
+ });
79
+ }
80
+ /**
81
+ * Validate cluster input
82
+ */
83
+ validateCluster(cluster) {
84
+ if (!cluster) {
85
+ return { valid: false, error: 'Cluster is null or undefined' };
86
+ }
87
+ if (!cluster.clusterId) {
88
+ return { valid: false, error: 'Missing cluster ID' };
89
+ }
90
+ // If no primary and no replicas, nothing to sync
91
+ if (!cluster.primary && cluster.replicas.length === 0) {
92
+ return { valid: false, error: 'Cluster has no instances' };
93
+ }
94
+ return { valid: true };
95
+ }
96
+ /**
97
+ * Execute the actual sync with lock protection
98
+ */
99
+ async executeSync(cluster, source, force) {
100
+ const clusterId = cluster.clusterId;
101
+ if (!this.enabled) {
102
+ logger.debug({ clusterId }, 'Sync is disabled');
103
+ return { skipped: true, reason: 'disabled', clusterId, source };
104
+ }
105
+ // Create the sync promise and acquire lock
106
+ const syncPromise = this.doSync(cluster, source, force);
107
+ this.locks.set(clusterId, syncPromise);
108
+ try {
109
+ return await syncPromise;
110
+ }
111
+ finally {
112
+ this.locks.delete(clusterId);
113
+ }
114
+ }
115
+ /**
116
+ * Perform the actual sync operation
117
+ */
118
+ async doSync(cluster, source, force) {
119
+ const clusterId = cluster.clusterId;
120
+ const now = Date.now();
121
+ const lastSync = this.lastSyncTime.get(clusterId) || 0;
122
+ // Check cooldown (unless forced)
123
+ if (!force && now - lastSync < this.cooldownMs) {
124
+ logger.debug({ clusterId, source, timeSinceLastSync: now - lastSync }, 'Sync skipped due to cooldown');
125
+ this.stats.skippedSyncs++;
126
+ return { skipped: true, reason: 'cooldown', clusterId, source };
127
+ }
128
+ // Check for topology changes (unless forced)
129
+ const topologyHash = this.computeTopologyHash(cluster);
130
+ if (!force && this.lastTopologyHash.get(clusterId) === topologyHash) {
131
+ logger.debug({ clusterId, source }, 'Sync skipped - no topology change');
132
+ this.stats.skippedSyncs++;
133
+ return { skipped: true, reason: 'no_change', clusterId, source };
134
+ }
135
+ // Perform the sync with retry
136
+ logger.info({ clusterId, source }, 'Syncing ProxySQL with cluster topology');
137
+ this.stats.totalSyncs++;
138
+ let lastError = null;
139
+ for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
140
+ try {
141
+ const proxysql = (0, proxysql_manager_js_1.getProxySQLManager)();
142
+ const result = await proxysql.syncCluster(cluster, this.writerHostgroup, this.readerHostgroup);
143
+ if (result.success) {
144
+ // Only update state on success
145
+ this.lastSyncTime.set(clusterId, Date.now());
146
+ this.lastTopologyHash.set(clusterId, topologyHash);
147
+ this.stats.successfulSyncs++;
148
+ this.stats.lastSyncAt = new Date();
149
+ this.stats.lastSyncCluster = clusterId;
150
+ logger.info({ clusterId, source, serversSynced: result.serversAdded }, 'ProxySQL sync completed successfully');
151
+ return {
152
+ skipped: false,
153
+ clusterId,
154
+ source,
155
+ serversSynced: result.serversAdded,
156
+ };
157
+ }
158
+ else {
159
+ lastError = result.errors.join(', ');
160
+ logger.warn({ clusterId, attempt, errors: result.errors }, 'Sync attempt failed');
161
+ }
162
+ }
163
+ catch (error) {
164
+ lastError = error instanceof Error ? error.message : String(error);
165
+ logger.error({ clusterId, attempt, error: lastError }, 'Sync attempt threw an error');
166
+ }
167
+ }
168
+ // All retries exhausted
169
+ this.stats.failedSyncs++;
170
+ logger.error({ clusterId, source, attempts: this.maxRetries, error: lastError }, 'All sync attempts failed');
171
+ return {
172
+ skipped: false,
173
+ reason: 'failed',
174
+ clusterId,
175
+ source,
176
+ error: lastError || 'Unknown error',
177
+ };
178
+ }
179
+ /**
180
+ * Compute a hash of the cluster topology for change detection
181
+ */
182
+ computeTopologyHash(cluster) {
183
+ const parts = [
184
+ cluster.clusterId,
185
+ cluster.primary ? `${cluster.primary.host}:${cluster.primary.port}:${cluster.primary.state}` : 'null',
186
+ ...cluster.replicas
187
+ .map(r => `${r.host}:${r.port}:${r.state}`)
188
+ .sort(),
189
+ ];
190
+ return parts.join('|');
191
+ }
192
+ /**
193
+ * Get sync statistics
194
+ */
195
+ getStats() {
196
+ return { ...this.stats };
197
+ }
198
+ /**
199
+ * Reset cooldown for a specific cluster
200
+ */
201
+ resetCooldown(clusterId) {
202
+ this.lastSyncTime.delete(clusterId);
203
+ logger.debug({ clusterId }, 'Cooldown reset for cluster');
204
+ }
205
+ /**
206
+ * Clear all cached topology hashes
207
+ */
208
+ clearCache() {
209
+ this.lastTopologyHash.clear();
210
+ this.lastSyncTime.clear();
211
+ logger.debug('Sync coordinator cache cleared');
212
+ }
213
+ /**
214
+ * Get the last sync time for a cluster
215
+ */
216
+ getLastSyncTime(clusterId) {
217
+ const timestamp = this.lastSyncTime.get(clusterId);
218
+ return timestamp ? new Date(timestamp) : null;
219
+ }
220
+ /**
221
+ * Check if a cluster is within cooldown period
222
+ */
223
+ isInCooldown(clusterId) {
224
+ const lastSync = this.lastSyncTime.get(clusterId);
225
+ if (!lastSync)
226
+ return false;
227
+ return Date.now() - lastSync < this.cooldownMs;
228
+ }
229
+ }
230
+ exports.SyncCoordinator = SyncCoordinator;
231
+ // Singleton instance
232
+ let syncCoordinator = null;
233
+ /**
234
+ * Get the sync coordinator instance
235
+ */
236
+ function getSyncCoordinator() {
237
+ if (!syncCoordinator) {
238
+ const settings = (0, settings_js_1.getSettings)();
239
+ syncCoordinator = new SyncCoordinator({
240
+ cooldownMs: settings.sync?.syncCooldownMs ?? 5000,
241
+ debounceMs: settings.sync?.debounceMs ?? 1000,
242
+ maxRetries: settings.sync?.maxRetries ?? 2,
243
+ enabled: settings.sync?.enabled ?? true,
244
+ });
245
+ }
246
+ return syncCoordinator;
247
+ }
248
+ /**
249
+ * Reset the sync coordinator (for testing)
250
+ */
251
+ function resetSyncCoordinator() {
252
+ syncCoordinator = null;
253
+ }
254
+ //# sourceMappingURL=sync-coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-coordinator.js","sourceRoot":"","sources":["../../../src/core/sync/sync-coordinator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * ClawSQL - Topology Watcher
3
+ *
4
+ * Polls Orchestrator for topology changes and triggers ProxySQL sync.
5
+ * Acts as a fallback mechanism when webhooks are not received.
6
+ * Also attempts to recover replicas stuck in maintenance state.
7
+ */
8
+ /**
9
+ * Topology Watcher
10
+ * Periodically checks for topology changes and syncs ProxySQL.
11
+ */
12
+ export declare class TopologyWatcher {
13
+ private interval;
14
+ private running;
15
+ private lastTopologyHashes;
16
+ private pollIntervalMs;
17
+ private enabled;
18
+ constructor(options?: {
19
+ pollIntervalMs?: number;
20
+ enabled?: boolean;
21
+ });
22
+ /**
23
+ * Start watching for topology changes
24
+ */
25
+ start(): Promise<void>;
26
+ /**
27
+ * Stop watching for topology changes
28
+ */
29
+ stop(): void;
30
+ /**
31
+ * Check if the watcher is running
32
+ */
33
+ isRunning(): boolean;
34
+ /**
35
+ * Perform a single poll of all clusters
36
+ */
37
+ private poll;
38
+ /**
39
+ * Compute a hash of the cluster topology
40
+ */
41
+ private computeTopologyHash;
42
+ /**
43
+ * Force a poll cycle
44
+ */
45
+ forcePoll(): Promise<void>;
46
+ /**
47
+ * Clear cached topology hashes
48
+ */
49
+ clearCache(): void;
50
+ /**
51
+ * Get the current poll interval
52
+ */
53
+ getPollInterval(): number;
54
+ /**
55
+ * Update the poll interval
56
+ */
57
+ setPollInterval(intervalMs: number): void;
58
+ /**
59
+ * Check if any replicas need recovery (maintenance or offline state)
60
+ */
61
+ private hasReplicasNeedingRecovery;
62
+ /**
63
+ * Attempt to recover replicas in maintenance state (non-blocking)
64
+ * If recovery succeeds, invalidates cache to trigger re-sync on next poll
65
+ */
66
+ private attemptReplicaRecovery;
67
+ }
68
+ /**
69
+ * Get the topology watcher instance
70
+ */
71
+ export declare function getTopologyWatcher(): TopologyWatcher;
72
+ /**
73
+ * Reset the topology watcher (for testing)
74
+ */
75
+ export declare function resetTopologyWatcher(): void;
76
+ //# sourceMappingURL=topology-watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topology-watcher.d.ts","sourceRoot":"","sources":["../../../src/core/sync/topology-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,kBAAkB,CAAkC;IAC5D,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,OAAO,CAAU;gBAEb,OAAO,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;IAKpE;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB5B;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;YACW,IAAI;IAmElB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAa3B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAUzC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAMlC;;;OAGG;IACH,OAAO,CAAC,sBAAsB;CAiC/B;AAKD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CASpD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAK3C"}