MyCATApache/Mycat-Server · critical · RuntimeException
migrate task ( ) check fail,because fromNode: ( ) and…
Error message
migrate task (${taskID}) check fail,because fromNode:${from}(${oldCount}) and toNode:${to}(${newCount}) and sql is ${sql} What it means
After migration completes but before committing the switch, check compares row counts of each task's source (from) and destination (to) node using the same COUNT SQL. A mismatch means data was lost or added during migration (e.g. writes to the source after the dump), so it throws this detailed error with task ID, both counts and the SQL. It blocks an unsafe switch.
Solutions
- Ensure writes to the source are stopped/fenced before migration, then rerun the check or the whole task.
- Verify the binlog replay finished successfully (check migration task status in ZK and logs for binlog errors).
- Run the reported count SQL manually on both nodes and reconcile the difference (re-dump the affected table if needed).
- Check makeCountSql correctness for the table's sharding column and re-execute the task from scratch if data drifted badly.
Example fix
// before
if (oldCount != newCount) {
throw new RuntimeException("migrate task ... check fail");
}
// after
// operational fix: fence writes, re-run task
// mysql> SELECT COUNT(*) FROM table WHERE <task range>; -- on from and to nodes
// if mismatch persists: mycat: clear task in ZK and restart migration with writes blocked Defensive patterns
Strategy: validation
Validate before calling
// before switching, fence writes and compare counts
long oldCnt = executeCount(sql, task.getFrom());
long newCnt = executeCount(sql, task.getTo());
if (oldCnt != newCnt) {
throw new IllegalStateException("counts differ: from=" + oldCnt + " to=" + newCnt);
} Try / catch
try { checkCommit(); } catch (RuntimeException e) {
if (e.getMessage().contains("check fail")) {
// do NOT switch; stop app writes, verify binlog replay, re-run task
}
throw e;
} Prevention
- Block application writes to the source table before/during migration
- Confirm binlog replay completed with zero errors before checkCommit
- Run a manual COUNT(*) on both nodes as preflight
- Monitor ZK task state; abort early on binlog stream errors
When it happens
Trigger: oldCount != newCount: rows were written to source tables after data dump started (binlog replay incomplete), the dump missed rows, or DELETE/UPDATE statements failed silently on the target.
Common situations: Application still writing to the source schema during migration; binlog stream stopped or errored partway (see related loadColumn failure); filtered/incorrect WHERE in count SQL due to sharding column misconfig.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
AI-assisted analysis of MyCATApache/Mycat-Server@65f8d8beb7 (2026-09-11).
Data as JSON: /api/errors/76af3ae7d5c83ebe.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/io/mycat/migrate/SwitchCommitListener.java:288
}
private void clean(String taskID, List<MigrateTask> allTaskList) throws IOException, SQLException {
//clean
for (MigrateTask migrateTask : allTaskList) {
String sql = makeCleanSql(migrateTask);
MigrateUtils.execulteSql(sql, migrateTask.getFrom());
}
}
private void check(String taskID, List<MigrateTask> allTaskList) throws SQLException, IOException {
for (MigrateTask migrateTask : allTaskList) {
String sql = MigrateUtils.makeCountSql(migrateTask);
long oldCount = MigrateUtils.execulteCount(sql, migrateTask.getFrom());
long newCount = MigrateUtils.execulteCount(sql, migrateTask.getTo());
if (oldCount != newCount) {
throw new RuntimeException("migrate task (" + taskID + ") check fail,because fromNode:"
+ migrateTask.getFrom() + "(" + oldCount + ")" + " and toNode:" + migrateTask.getTo() + "(" + newCount + ") and sql is " + sql);
}
}
}
private String makeCleanSql(MigrateTask task) {
StringBuilder sb = new StringBuilder();
sb.append("delete from ");
sb.append(task.getTable()).append(" where ");
List<Range> slots = task.getSlots();
for (int i = 0; i < slots.size(); i++) {
Range range = slots.get(i);
if (i != 0)
sb.append(" or ");
if (range.start == range.end) {
sb.append(" _slot=").append(range.start);
} else {
sb.append("( _slot>=").append(range.start);View on GitHub (pinned to 65f8d8beb7)