diff --git a/apps/api/src/ops/ops.service.ts b/apps/api/src/ops/ops.service.ts index 532a86d..509379a 100644 --- a/apps/api/src/ops/ops.service.ts +++ b/apps/api/src/ops/ops.service.ts @@ -262,9 +262,26 @@ export class OpsService implements OnModuleInit { * deploy/scripts/pre-migrate-backup.mjs — the two write into the same volume * and both are listed as restore points by this same screen. * - * --set-gtid-purged=OFF: the production server is the replication SOURCE with - * GTID on, so without it every dump embeds SET @@GLOBAL.GTID_PURGED and is - * unrestorable onto the very server it came from. + * The dumper is probed at runtime rather than assumed. This command runs + * inside the API image, whose `mysql-client` is Alpine's — i.e. MariaDB's — + * where `mysqldump` is a deprecation-warning shim over `mariadb-dump` that + * rejects --set-gtid-purged outright: + * mysqldump: unknown variable 'set-gtid-purged=OFF' + * which failed every backup, including the safety backups SYNC and REIMPORT + * take first. MariaDB's dumper emits no GTID state unless asked (--gtid), so + * there is nothing to suppress there; the flag is passed only when the dumper + * on PATH advertises it, and the real binary is called directly only in the + * MariaDB case (calling `mariadb-dump` whenever it merely exists would pick + * it over a MySQL `mysqldump` earlier in PATH on a host carrying both). + * + * The probe is a command substitution, not `--help | grep -q`: PIPEFAIL is in + * effect and grep closing the pipe early would make a supported flag look + * unsupported. + * + * --set-gtid-purged=OFF (MySQL only): the production server is the + * replication SOURCE with GTID on, so without it every dump embeds + * SET @@GLOBAL.GTID_PURGED and is unrestorable onto the very server it came + * from. * * The table-count assertion is not belt-and-braces: `gzip -t` passes on the * ~372-byte output of a mysqldump that died on its first statement, so a @@ -277,8 +294,12 @@ export class OpsService implements OnModuleInit { */ private dumpCommand(flags: string, db: string, out: string): string { return ( - `( mysqldump ${flags} --single-transaction --routines --triggers ` + - `--no-tablespaces --set-gtid-purged=OFF ${db} | gzip -c > ${out} && ` + + `DUMP=mysqldump; GTID=; ` + + `case "$(mysqldump --help 2>/dev/null || true)" in ` + + `*set-gtid-purged*) GTID=--set-gtid-purged=OFF;; ` + + `*) command -v mariadb-dump >/dev/null 2>&1 && DUMP=mariadb-dump;; esac; ` + + `( $DUMP ${flags} --single-transaction --routines --triggers ` + + `--no-tablespaces $GTID ${db} | gzip -c > ${out} && ` + `gzip -t ${out} && ` + `TABLAS=$(gunzip -c ${out} | grep -c 'CREATE TABLE') && ` + `echo "tablas capturadas: $TABLAS" && ` + diff --git a/docs/DEPLOY_AND_MIGRATIONS.md b/docs/DEPLOY_AND_MIGRATIONS.md index 8298f65..396fba7 100644 --- a/docs/DEPLOY_AND_MIGRATIONS.md +++ b/docs/DEPLOY_AND_MIGRATIONS.md @@ -294,10 +294,19 @@ a warning, which is what local development wants. Two more things the panel's dumps now do, for the same reasons the pre-migrate backup does them (see `deploy/scripts/pre-migrate-backup.mjs`): -- **`--set-gtid-purged=OFF`.** galactus is the replication *source* with GTID - on, so without this every dump embeds `SET @@GLOBAL.GTID_PURGED` and cannot be - restored onto the server it came from — which is precisely what the restore - screen exists to do. +- **`--set-gtid-purged=OFF`, but only when the dumper supports it.** galactus is + the replication *source* with GTID on, so on a MySQL client this flag is what + keeps every dump from embedding `SET @@GLOBAL.GTID_PURGED` and becoming + unrestorable onto the server it came from — which is precisely what the + restore screen exists to do. The panel, however, dumps from *inside the API + container*, where Alpine's `mysql-client` is MariaDB's: there `mysqldump` is a + shim over `mariadb-dump`, the flag does not exist, and passing it failed every + backup with `mysqldump: unknown variable 'set-gtid-purged=OFF'`. So the panel + probes `mysqldump --help` and passes the flag only if it is advertised, + invoking `mariadb-dump` directly otherwise (MariaDB writes no GTID state + unless asked with `--gtid`, so there is nothing to suppress). The pre-migrate + backup keeps the flag unconditionally — it runs in a real `mysql:8.4` image, + not in the API container. - **`set -o pipefail` and a `CREATE TABLE` count.** `mysqldump | gzip` reports gzip's exit status, and a `mysqldump` that dies on its first statement still produces a ~372-byte perfectly valid archive that passes `gzip -t`. Without