fix(ops): stop the replica field parser reading the next line
The panel reported "Error SQL: Replicate_Ignore_Server_Ids:" against a replica that was healthy — both threads running, zero lag. `\s` matches newlines in JavaScript, so `^\s*NAME:\s*(.*)$` let the `\s*` after the colon walk past an EMPTY field's line break and capture the following line. Last_SQL_Error is blank on a healthy replica and Replicate_Ignore_Server_Ids happens to be printed immediately after it, so the blank error field returned the next field's name as its value. Every empty field was affected; the visible damage was that a healthy replica rendered as broken, which is the worst direction for a health panel to fail. Fixed with `[^\S\n]` — horizontal whitespace only — on both sides of the field name. Extracted as replicaField() and pinned by replication.spec.ts against the verbatim output of the live replica, keeping the empty Last_SQL_Error adjacent to Replicate_Ignore_Server_Ids because that exact adjacency is what broke. Also covers the literal "NULL" lag surviving as a distinct value from empty, and a field name that is a suffix of another (Last_Error vs Last_SQL_Error) not matching the wrong line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -100,11 +100,7 @@ export class ReplicationService {
|
||||
return { ...empty, configured: true, problem: `No se pudo conectar: ${msg}` };
|
||||
}
|
||||
|
||||
const field = (name: string): string | null => {
|
||||
const m = raw.match(new RegExp(`^\\s*${name}:\\s*(.*)$`, "m"));
|
||||
const v = m?.[1]?.trim();
|
||||
return v === undefined || v === "" ? null : v;
|
||||
};
|
||||
const field = (name: string): string | null => replicaField(raw, name);
|
||||
|
||||
// An empty result set means the server is not configured as a replica at
|
||||
// all — distinct from "configured but broken", and worth saying plainly.
|
||||
@@ -151,3 +147,25 @@ export class ReplicationService {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one field out of `SHOW REPLICA STATUS\G` output.
|
||||
*
|
||||
* Exported for testing, and worth testing: the obvious regex is wrong.
|
||||
* `\s` matches newlines in JavaScript, so `^\s*NAME:\s*(.*)$` lets the `\s*`
|
||||
* after the colon swallow the line break of an EMPTY field and capture the
|
||||
* following line instead. Last_SQL_Error is empty on a healthy replica, so that
|
||||
* version reported the next line ("Replicate_Ignore_Server_Ids:") as a SQL
|
||||
* error and rendered a perfectly healthy replica as broken.
|
||||
*
|
||||
* Hence `[^\S\n]` — horizontal whitespace only — on both sides of the name.
|
||||
*
|
||||
* @returns the trimmed value, or null when the field is absent OR empty. Empty
|
||||
* and absent mean the same thing to every caller here: MySQL prints
|
||||
* error fields as blank rather than omitting them.
|
||||
*/
|
||||
export function replicaField(raw: string, name: string): string | null {
|
||||
const m = raw.match(new RegExp(`^[^\\S\\n]*${name}:[^\\S\\n]*(.*)$`, "m"));
|
||||
const v = m?.[1]?.trim();
|
||||
return v === undefined || v === "" ? null : v;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user