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.
- package/.env.example +97 -0
- package/README.md +372 -0
- package/dist/__tests__/config/settings.test.d.ts +5 -0
- package/dist/__tests__/config/settings.test.d.ts.map +1 -0
- package/dist/__tests__/config/settings.test.js +154 -0
- package/dist/__tests__/config/settings.test.js.map +1 -0
- package/dist/__tests__/core/discovery/topology.test.d.ts +5 -0
- package/dist/__tests__/core/discovery/topology.test.d.ts.map +1 -0
- package/dist/__tests__/core/discovery/topology.test.js +191 -0
- package/dist/__tests__/core/discovery/topology.test.js.map +1 -0
- package/dist/__tests__/core/failover/executor.test.d.ts +5 -0
- package/dist/__tests__/core/failover/executor.test.d.ts.map +1 -0
- package/dist/__tests__/core/failover/executor.test.js +256 -0
- package/dist/__tests__/core/failover/executor.test.js.map +1 -0
- package/dist/__tests__/core/monitoring/collector.test.d.ts +5 -0
- package/dist/__tests__/core/monitoring/collector.test.d.ts.map +1 -0
- package/dist/__tests__/core/monitoring/collector.test.js +131 -0
- package/dist/__tests__/core/monitoring/collector.test.js.map +1 -0
- package/dist/__tests__/core/monitoring/exporters.test.d.ts +5 -0
- package/dist/__tests__/core/monitoring/exporters.test.d.ts.map +1 -0
- package/dist/__tests__/core/monitoring/exporters.test.js +90 -0
- package/dist/__tests__/core/monitoring/exporters.test.js.map +1 -0
- package/dist/__tests__/core/routing/proxysql-manager.test.d.ts +5 -0
- package/dist/__tests__/core/routing/proxysql-manager.test.d.ts.map +1 -0
- package/dist/__tests__/core/routing/proxysql-manager.test.js +155 -0
- package/dist/__tests__/core/routing/proxysql-manager.test.js.map +1 -0
- package/dist/__tests__/types/index.test.d.ts +5 -0
- package/dist/__tests__/types/index.test.d.ts.map +1 -0
- package/dist/__tests__/types/index.test.js +290 -0
- package/dist/__tests__/types/index.test.js.map +1 -0
- package/dist/__tests__/utils/exceptions.test.d.ts +5 -0
- package/dist/__tests__/utils/exceptions.test.d.ts.map +1 -0
- package/dist/__tests__/utils/exceptions.test.js +142 -0
- package/dist/__tests__/utils/exceptions.test.js.map +1 -0
- package/dist/api/routes/clusters.d.ts +7 -0
- package/dist/api/routes/clusters.d.ts.map +1 -0
- package/dist/api/routes/clusters.js +123 -0
- package/dist/api/routes/clusters.js.map +1 -0
- package/dist/api/routes/config.d.ts +7 -0
- package/dist/api/routes/config.d.ts.map +1 -0
- package/dist/api/routes/config.js +65 -0
- package/dist/api/routes/config.js.map +1 -0
- package/dist/api/routes/failover.d.ts +7 -0
- package/dist/api/routes/failover.d.ts.map +1 -0
- package/dist/api/routes/failover.js +100 -0
- package/dist/api/routes/failover.js.map +1 -0
- package/dist/api/routes/instances.d.ts +11 -0
- package/dist/api/routes/instances.d.ts.map +1 -0
- package/dist/api/routes/instances.js +315 -0
- package/dist/api/routes/instances.js.map +1 -0
- package/dist/api/routes/monitoring.d.ts +7 -0
- package/dist/api/routes/monitoring.d.ts.map +1 -0
- package/dist/api/routes/monitoring.js +72 -0
- package/dist/api/routes/monitoring.js.map +1 -0
- package/dist/api/routes/webhooks.d.ts +12 -0
- package/dist/api/routes/webhooks.d.ts.map +1 -0
- package/dist/api/routes/webhooks.js +232 -0
- package/dist/api/routes/webhooks.js.map +1 -0
- package/dist/api/schemas/index.d.ts +965 -0
- package/dist/api/schemas/index.d.ts.map +1 -0
- package/dist/api/schemas/index.js +171 -0
- package/dist/api/schemas/index.js.map +1 -0
- package/dist/app.d.ts +13 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +197 -0
- package/dist/app.js.map +1 -0
- package/dist/bin/clawsql.d.ts +12 -0
- package/dist/bin/clawsql.d.ts.map +1 -0
- package/dist/bin/clawsql.js +43 -0
- package/dist/bin/clawsql.js.map +1 -0
- package/dist/cli/agent/handler.d.ts +73 -0
- package/dist/cli/agent/handler.d.ts.map +1 -0
- package/dist/cli/agent/handler.js +258 -0
- package/dist/cli/agent/handler.js.map +1 -0
- package/dist/cli/agent/index.d.ts +14 -0
- package/dist/cli/agent/index.d.ts.map +1 -0
- package/dist/cli/agent/index.js +30 -0
- package/dist/cli/agent/index.js.map +1 -0
- package/dist/cli/agent/openclaw-integration.d.ts +81 -0
- package/dist/cli/agent/openclaw-integration.d.ts.map +1 -0
- package/dist/cli/agent/openclaw-integration.js +341 -0
- package/dist/cli/agent/openclaw-integration.js.map +1 -0
- package/dist/cli/agent/providers/anthropic.d.ts +27 -0
- package/dist/cli/agent/providers/anthropic.d.ts.map +1 -0
- package/dist/cli/agent/providers/anthropic.js +106 -0
- package/dist/cli/agent/providers/anthropic.js.map +1 -0
- package/dist/cli/agent/providers/base.d.ts +91 -0
- package/dist/cli/agent/providers/base.d.ts.map +1 -0
- package/dist/cli/agent/providers/base.js +24 -0
- package/dist/cli/agent/providers/base.js.map +1 -0
- package/dist/cli/agent/providers/openai.d.ts +27 -0
- package/dist/cli/agent/providers/openai.d.ts.map +1 -0
- package/dist/cli/agent/providers/openai.js +98 -0
- package/dist/cli/agent/providers/openai.js.map +1 -0
- package/dist/cli/agent/tools/index.d.ts +32 -0
- package/dist/cli/agent/tools/index.d.ts.map +1 -0
- package/dist/cli/agent/tools/index.js +263 -0
- package/dist/cli/agent/tools/index.js.map +1 -0
- package/dist/cli/commands/cleanup.d.ts +12 -0
- package/dist/cli/commands/cleanup.d.ts.map +1 -0
- package/dist/cli/commands/cleanup.js +205 -0
- package/dist/cli/commands/cleanup.js.map +1 -0
- package/dist/cli/commands/clusters.d.ts +12 -0
- package/dist/cli/commands/clusters.d.ts.map +1 -0
- package/dist/cli/commands/clusters.js +468 -0
- package/dist/cli/commands/clusters.js.map +1 -0
- package/dist/cli/commands/config.d.ts +12 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +406 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/cron.d.ts +12 -0
- package/dist/cli/commands/cron.d.ts.map +1 -0
- package/dist/cli/commands/cron.js +215 -0
- package/dist/cli/commands/cron.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +13 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +687 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/failover.d.ts +16 -0
- package/dist/cli/commands/failover.d.ts.map +1 -0
- package/dist/cli/commands/failover.js +333 -0
- package/dist/cli/commands/failover.js.map +1 -0
- package/dist/cli/commands/health.d.ts +12 -0
- package/dist/cli/commands/health.d.ts.map +1 -0
- package/dist/cli/commands/health.js +125 -0
- package/dist/cli/commands/health.js.map +1 -0
- package/dist/cli/commands/help.d.ts +12 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +52 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/instances.d.ts +12 -0
- package/dist/cli/commands/instances.d.ts.map +1 -0
- package/dist/cli/commands/instances.js +801 -0
- package/dist/cli/commands/instances.js.map +1 -0
- package/dist/cli/commands/notify.d.ts +12 -0
- package/dist/cli/commands/notify.d.ts.map +1 -0
- package/dist/cli/commands/notify.js +43 -0
- package/dist/cli/commands/notify.js.map +1 -0
- package/dist/cli/commands/sql.d.ts +12 -0
- package/dist/cli/commands/sql.d.ts.map +1 -0
- package/dist/cli/commands/sql.js +90 -0
- package/dist/cli/commands/sql.js.map +1 -0
- package/dist/cli/commands/start.d.ts +12 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +174 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +12 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +218 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +12 -0
- package/dist/cli/commands/stop.d.ts.map +1 -0
- package/dist/cli/commands/stop.js +128 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/topology.d.ts +12 -0
- package/dist/cli/commands/topology.d.ts.map +1 -0
- package/dist/cli/commands/topology.js +106 -0
- package/dist/cli/commands/topology.js.map +1 -0
- package/dist/cli/completer.d.ts +47 -0
- package/dist/cli/completer.d.ts.map +1 -0
- package/dist/cli/completer.js +332 -0
- package/dist/cli/completer.js.map +1 -0
- package/dist/cli/formatter.d.ts +165 -0
- package/dist/cli/formatter.d.ts.map +1 -0
- package/dist/cli/formatter.js +408 -0
- package/dist/cli/formatter.js.map +1 -0
- package/dist/cli/index.d.ts +21 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +79 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/raw-input.d.ts +97 -0
- package/dist/cli/raw-input.d.ts.map +1 -0
- package/dist/cli/raw-input.js +493 -0
- package/dist/cli/raw-input.js.map +1 -0
- package/dist/cli/registry.d.ts +103 -0
- package/dist/cli/registry.d.ts.map +1 -0
- package/dist/cli/registry.js +205 -0
- package/dist/cli/registry.js.map +1 -0
- package/dist/cli/repl.d.ts +83 -0
- package/dist/cli/repl.d.ts.map +1 -0
- package/dist/cli/repl.js +447 -0
- package/dist/cli/repl.js.map +1 -0
- package/dist/cli/ui/components.d.ts +144 -0
- package/dist/cli/ui/components.d.ts.map +1 -0
- package/dist/cli/ui/components.js +331 -0
- package/dist/cli/ui/components.js.map +1 -0
- package/dist/cli/ui/index.d.ts +7 -0
- package/dist/cli/ui/index.d.ts.map +1 -0
- package/dist/cli/ui/index.js +23 -0
- package/dist/cli/ui/index.js.map +1 -0
- package/dist/cli/utils/docker-files.d.ts +39 -0
- package/dist/cli/utils/docker-files.d.ts.map +1 -0
- package/dist/cli/utils/docker-files.js +223 -0
- package/dist/cli/utils/docker-files.js.map +1 -0
- package/dist/cli/utils/docker-prereq.d.ts +48 -0
- package/dist/cli/utils/docker-prereq.d.ts.map +1 -0
- package/dist/cli/utils/docker-prereq.js +203 -0
- package/dist/cli/utils/docker-prereq.js.map +1 -0
- package/dist/config/settings.d.ts +594 -0
- package/dist/config/settings.d.ts.map +1 -0
- package/dist/config/settings.js +250 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/core/discovery/cluster-view.d.ts +50 -0
- package/dist/core/discovery/cluster-view.d.ts.map +1 -0
- package/dist/core/discovery/cluster-view.js +235 -0
- package/dist/core/discovery/cluster-view.js.map +1 -0
- package/dist/core/discovery/scanner.d.ts +70 -0
- package/dist/core/discovery/scanner.d.ts.map +1 -0
- package/dist/core/discovery/scanner.js +197 -0
- package/dist/core/discovery/scanner.js.map +1 -0
- package/dist/core/discovery/topology.d.ts +118 -0
- package/dist/core/discovery/topology.d.ts.map +1 -0
- package/dist/core/discovery/topology.js +550 -0
- package/dist/core/discovery/topology.js.map +1 -0
- package/dist/core/failover/candidate-selector.d.ts +46 -0
- package/dist/core/failover/candidate-selector.d.ts.map +1 -0
- package/dist/core/failover/candidate-selector.js +70 -0
- package/dist/core/failover/candidate-selector.js.map +1 -0
- package/dist/core/failover/executor.d.ts +104 -0
- package/dist/core/failover/executor.d.ts.map +1 -0
- package/dist/core/failover/executor.js +248 -0
- package/dist/core/failover/executor.js.map +1 -0
- package/dist/core/failover/operation-builder.d.ts +71 -0
- package/dist/core/failover/operation-builder.d.ts.map +1 -0
- package/dist/core/failover/operation-builder.js +157 -0
- package/dist/core/failover/operation-builder.js.map +1 -0
- package/dist/core/failover/operation-runner.d.ts +75 -0
- package/dist/core/failover/operation-runner.d.ts.map +1 -0
- package/dist/core/failover/operation-runner.js +191 -0
- package/dist/core/failover/operation-runner.js.map +1 -0
- package/dist/core/failover/promoter.d.ts +33 -0
- package/dist/core/failover/promoter.d.ts.map +1 -0
- package/dist/core/failover/promoter.js +97 -0
- package/dist/core/failover/promoter.js.map +1 -0
- package/dist/core/failover/recovery-manager.d.ts +47 -0
- package/dist/core/failover/recovery-manager.d.ts.map +1 -0
- package/dist/core/failover/recovery-manager.js +145 -0
- package/dist/core/failover/recovery-manager.js.map +1 -0
- package/dist/core/failover/types.d.ts +54 -0
- package/dist/core/failover/types.d.ts.map +1 -0
- package/dist/core/failover/types.js +8 -0
- package/dist/core/failover/types.js.map +1 -0
- package/dist/core/monitoring/collector.d.ts +25 -0
- package/dist/core/monitoring/collector.d.ts.map +1 -0
- package/dist/core/monitoring/collector.js +115 -0
- package/dist/core/monitoring/collector.js.map +1 -0
- package/dist/core/monitoring/exporters.d.ts +49 -0
- package/dist/core/monitoring/exporters.d.ts.map +1 -0
- package/dist/core/monitoring/exporters.js +126 -0
- package/dist/core/monitoring/exporters.js.map +1 -0
- package/dist/core/routing/proxysql-manager.d.ts +213 -0
- package/dist/core/routing/proxysql-manager.d.ts.map +1 -0
- package/dist/core/routing/proxysql-manager.js +632 -0
- package/dist/core/routing/proxysql-manager.js.map +1 -0
- package/dist/core/sync/replica-recovery.d.ts +40 -0
- package/dist/core/sync/replica-recovery.d.ts.map +1 -0
- package/dist/core/sync/replica-recovery.js +134 -0
- package/dist/core/sync/replica-recovery.js.map +1 -0
- package/dist/core/sync/sync-coordinator.d.ts +83 -0
- package/dist/core/sync/sync-coordinator.d.ts.map +1 -0
- package/dist/core/sync/sync-coordinator.js +254 -0
- package/dist/core/sync/sync-coordinator.js.map +1 -0
- package/dist/core/sync/topology-watcher.d.ts +76 -0
- package/dist/core/sync/topology-watcher.d.ts.map +1 -0
- package/dist/core/sync/topology-watcher.js +222 -0
- package/dist/core/sync/topology-watcher.js.map +1 -0
- package/dist/core/sync/types.d.ts +85 -0
- package/dist/core/sync/types.d.ts.map +1 -0
- package/dist/core/sync/types.js +8 -0
- package/dist/core/sync/types.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +212 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +153 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/database.d.ts +62 -0
- package/dist/utils/database.d.ts.map +1 -0
- package/dist/utils/database.js +257 -0
- package/dist/utils/database.js.map +1 -0
- package/dist/utils/exceptions.d.ts +69 -0
- package/dist/utils/exceptions.d.ts.map +1 -0
- package/dist/utils/exceptions.js +121 -0
- package/dist/utils/exceptions.js.map +1 -0
- package/dist/utils/logger.d.ts +20 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +90 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/mysql-client.d.ts +43 -0
- package/dist/utils/mysql-client.d.ts.map +1 -0
- package/dist/utils/mysql-client.js +125 -0
- package/dist/utils/mysql-client.js.map +1 -0
- package/docker/Dockerfile +61 -0
- package/docker/Dockerfile.node +41 -0
- package/docker/grafana/dashboards/clawsql.json +212 -0
- package/docker/grafana/provisioning/dashboards/dashboards.yml +13 -0
- package/docker/grafana/provisioning/datasources/datasources.yml +12 -0
- package/docker/init/primary.sql +26 -0
- package/docker/init/replica.sql +16 -0
- package/docker/orchestrator/orchestrator.conf.json +98 -0
- package/docker/prometheus/prometheus.yml +45 -0
- package/docker/proxysql/entrypoint.sh +8 -0
- package/docker/proxysql/init.sql.demo +30 -0
- package/docker/proxysql/proxysql.cnf +38 -0
- package/docker-compose.demo.yml +115 -0
- package/docker-compose.yml +217 -0
- package/init/primary.sql +19 -0
- package/init/replica.sql +13 -0
- 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":"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"}
|
|
@@ -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"}
|