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

  1. Ensure writes to the source are stopped/fenced before migration, then rerun the check or the whole task.
  2. Verify the binlog replay finished successfully (check migration task status in ZK and logs for binlog errors).
  3. Run the reported count SQL manually on both nodes and reconcile the difference (re-dump the affected table if needed).
  4. 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

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)