pingcap / pingcap/ticdc

redo: redo apply may OOM when applying a large DML-only range

Open
#5,938 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

type/enhancement
Dominant language
Go
Stars
56
Forks
63
Avg merge
2d 20h
Merged PRs (30d)
34

Description

What did you do?

Run cdc redo apply on a redo log range that contains a large amount of DML. The risk is higher when:

  • the gap between checkpointTs and resolvedTs is large;
  • the upstream workload has high write throughput or wide rows;
  • the range contains no DDL, or DDL events are infrequent;
  • the redo apply process has a constrained memory limit.

The buffering model was introduced by redo: rewrite redo apply (#2167).

Before that rewrite, redo apply used a 1 GiB MemQuota and BlockAcquire to apply backpressure while rows were being sent to table sinks. The rewrite removed this quota path and introduced a per-table eventsGroup.

The current flow is:

  1. consumeLogs reads rows continuously.
  2. applyRow converts each redo row to a DMLEvent and appends it to the table's eventsGroup.
  3. The buffered events are passed to the MySQL sink only when a relevant DDL is processed or after the reader reaches the end of the redo range.

Relevant code:

What did you expect to see?

Redo apply should keep memory usage bounded while processing a large redo range. It should flush DML in bounded batches or apply backpressure based on the buffered bytes, while preserving transaction ordering and DDL boundaries.

What did you see instead?

For a DML-only range, redo apply retains the converted DML events until all row logs have been read. Memory usage therefore grows with the total DML volume between checkpointTs and resolvedTs, rather than with a bounded in-flight batch.

Merging rows with the same commit timestamp reduces the number of DMLEvent objects, but the merged row chunks and metadata remain live. With a sufficiently large redo range, the process may exhaust the container memory and be terminated by OOM.

The --memory-limit option sets Go's soft memory limit. It does not provide admission control for these live events and cannot reclaim them while they remain referenced by eventsGroup.

Versions of the cluster

Upstream TiDB cluster version:

N/A

Upstream TiKV version:

N/A

TiCDC version:

master

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in pkg/applier/redo.go at consumeLogs, applyRow, and waitTableFlush, then inspect eventsGroup in pkg/applier/helper.go and ToDMLEvent in pkg/common/event/redo.go. Trace how DML-only ranges retain events and identify a bounded flush or backpressure point that preserves transaction ordering and DDL boundaries. Done means memory remains bounded while the full redo range is applied correctly.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.