118 lines
3.6 KiB
TypeScript
118 lines
3.6 KiB
TypeScript
import mysql, { Connection } from "mysql";
|
|
import { exec } from "child_process";
|
|
import { promisify } from "util";
|
|
|
|
// Configuration interface
|
|
interface DatabaseConfig {
|
|
host: string;
|
|
user: string;
|
|
password: string;
|
|
database?: string; // Optional for global connection
|
|
}
|
|
|
|
// Master status interface
|
|
interface MasterStatus {
|
|
File: string;
|
|
Position: number;
|
|
Binlog_Do_DB?: string;
|
|
Binlog_Ignore_DB?: string;
|
|
}
|
|
|
|
function getConnection(config: DatabaseConfig): Connection {
|
|
return mysql.createConnection(config);
|
|
}
|
|
|
|
function getMasterStatus(config: DatabaseConfig): Promise<MasterStatus> {
|
|
return new Promise((resolve, reject) => {
|
|
const connection = getConnection(config);
|
|
connection.query("SHOW MASTER STATUS", (error, results) => {
|
|
connection.end();
|
|
if (error) reject(error);
|
|
else resolve(results[0] as MasterStatus);
|
|
});
|
|
});
|
|
}
|
|
|
|
async function syncDatabases() {
|
|
const config: DatabaseConfig = {
|
|
host: "localhost",
|
|
user: "root",
|
|
password: "your_password",
|
|
};
|
|
|
|
let lastPosition: number | null = null; // Track last synced position
|
|
|
|
while (true) {
|
|
try {
|
|
// Get current master status
|
|
const { File, Position } = await getMasterStatus(config);
|
|
|
|
// Determine start position (use lastPosition or 4 if first run)
|
|
const startPosition = lastPosition !== null ? lastPosition + 1 : 4;
|
|
if (startPosition >= Position) {
|
|
await new Promise((resolve) => setTimeout(resolve, 5000)); // Wait 5 seconds if no new changes
|
|
continue;
|
|
}
|
|
|
|
// Execute mysqlbinlog to get changes
|
|
const execPromise = promisify(exec);
|
|
const { stdout } = await execPromise(
|
|
`mysqlbinlog --database=db_master ${File} --start-position=${startPosition} --stop-position=${Position}`
|
|
);
|
|
|
|
if (stdout) {
|
|
const connection = getConnection({
|
|
...config,
|
|
database: "db_slave",
|
|
});
|
|
return new Promise((resolve, reject) => {
|
|
connection.query(stdout, (error) => {
|
|
connection.end();
|
|
if (error) reject(error);
|
|
else {
|
|
lastPosition = Position;
|
|
console.log(
|
|
`Synced up to position ${Position} at ${new Date().toISOString()}`
|
|
);
|
|
resolve(null);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error("Sync error:", error);
|
|
}
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 5000)); // Check every 5 seconds
|
|
}
|
|
}
|
|
|
|
// Initialize db_slave with db_master data
|
|
async function initializeSlave() {
|
|
const config: DatabaseConfig = {
|
|
host: "localhost",
|
|
user: "root",
|
|
password: "your_password",
|
|
};
|
|
|
|
try {
|
|
await promisify(exec)(
|
|
`mysqldump -u ${config.user} -p${config.password} db_master > db_master_backup.sql`
|
|
);
|
|
await promisify(exec)(
|
|
`mysql -u ${config.user} -p${config.password} db_slave < db_master_backup.sql`
|
|
);
|
|
console.log("Slave initialized with master data");
|
|
} catch (error) {
|
|
console.error("Initialization error:", error);
|
|
}
|
|
}
|
|
|
|
// Run the sync process
|
|
async function main() {
|
|
await initializeSlave();
|
|
await syncDatabases();
|
|
}
|
|
|
|
main().catch(console.error);
|