alibaba/canal · error · CanalStoreException

no match ack position

Error message

no match ack position

What it means

Thrown in MemoryEventStoreWithBuffer.cleanUntil (invoked by ack) when, after scanning every event between ackSequence+1 and getSequence (or seqId), no event matches the supplied LogPosition via CanalEventUtils.checkPosition. The position being acked is not present in the ring buffer — it was already overwritten by newer events, already acked, or belongs to a different instance.

Source

Thrown at store/src/main/java/com/alibaba/otter/canal/store/memory/MemoryEventStoreWithBuffer.java:477

                        // 考虑getFirstPosition/getLastPosition会获取最后一次ack的position信息
                        // ack清理的时候只处理entry=null,释放内存
                        Event lastEvent = entries[getIndex(next)];
                        lastEvent.setEntry(null);
                        lastEvent.setRawEntry(null);
                    }

                    if (ackSequence.compareAndSet(sequence, next)) {// 避免并发ack
                        notFull.signal();
                        ackTableRows.addAndGet(deltaRows);
                        if (localExecTime > 0) {
                            ackExecTime.lazySet(localExecTime);
                        }
                        return;
                    }
                }
            }
            if (!hasMatch) {// 找不到对应需要ack的position
                throw new CanalStoreException("no match ack position" + position.toString());
            }
        } finally {
            lock.unlock();
        }
    }

    public void rollback() throws CanalStoreException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            getSequence.set(ackSequence.get());
            getMemSize.set(ackMemSize.get());
        } finally {
            lock.unlock();
        }
    }

    public void cleanAll() throws CanalStoreException {

View on GitHub (pinned to 87be50e876)

Solutions

  1. Increase canal.instance.memory.buffer.size (to a power of two) so acked positions stay resident until the client acks.
  2. Ensure the client acks promptly (lower ack latency) so the buffer does not rotate past unacked positions.
  3. Verify the position passed to ack is the one returned by the most recent get() for this instance, not a cached/stale cursor.
  4. After a buffer-rotation data-loss event, reset the client cursor and re-subscribe rather than retrying the stale ack.

Example fix

// before: acking a stale position after buffer rotated
server.ack(oldPosition); // throws no match ack position
// after: size buffer to throughput and ack fresh positions
canal.instance.memory.buffer.size = 16384
// in client: ack immediately after processing the latest get()
server.ack(latestMessage.getPosition());
Defensive patterns

Strategy: validation

Validate before calling

// before acking, confirm the position is still within the buffer window
Position first = eventStore.getFirstPosition();
Position last = eventStore.getLastPosition();
if (first == null || CanalEventUtils.min((LogPosition) position, (LogPosition) first) == position) {
    // position is at or behind the first retained -> already evicted/acked
    logger.warn("ack position {} is behind buffer first {}, skipping", position, first);
    return;
}

Try / catch

try {
    eventStore.ack(position);
} catch (CanalStoreException e) {
    if (e.getMessage().contains("no match ack position")) {
        logger.warn("position {} no longer in buffer (rotated/acked), resetting cursor", position);
        server.rollback(clientId); // re-fetch from last acked position
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling ack(position) / cleanUntil with a LogPosition whose binlog filename+offset does not correspond to any entry currently held in the ring buffer. Happens when the buffer has advanced past that position (events evicted), or when the position came from a stale/foreign cursor.

Common situations: Client acks a very old batch whose events were already evicted from the fixed-size ring buffer; bufferSize too small for the throughput so entries rotate before ack; mixing positions across canal instances or after a cursor reset; duplicate ack after the position was already cleaned.

Related errors


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/086dca44734557ac. Report an issue: GitHub.