pingcap / pingcap/tidb

[import] IMPORT INTO can silently skip new input after checkpoint cleanup failure

Open
#70,586 1 comment 0 reactions 1 assignee Claimed by @D3Hunter View on GitHub
component/import found-by-ai may-affects-25.10 may-affects-26.3 may-affects-7.5 may-affects-8.1 may-affects-8.5 severity/minor type/bug
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

# [import] IMPORT INTO can silently skip new input after checkpoint cleanup failure

## Bug Report

### 1. Minimal reproduce step (Required)

This issue has two parts that connect into one common-fault path:

1. current code treats a failed checkpoint cleanup after success as only a warning and still returns success;
2. a retained finished checkpoint for the same table name makes a later run with different nonempty input submit zero jobs and still return success.

The second part is already reproducible on current master with the `lightning/importinto` unit matrix:

- no checkpoint: one submission happens
- retained finished checkpoint + new nonempty input for the same table: `SubmitAndWait` returns `nil`, submissions = `0`

The current-code bridge for the first part is also present in `Importer.Run()`:

```go
if i.cfg.Checkpoint.Enable && i.cfg.Checkpoint.KeepAfterSuccess == config.CheckpointRemove {
i.logger.Info("removing all checkpoints")
if err := i.cpMgr.Remove(ctx, common.AllTables); err != nil {
i.logger.Warn("failed to remove checkpoints", zap.Error(err))
}
}
return nil
```

On the default `keep-after-success=remove` path, a common DB/storage/network failure in `cpMgr.Remove()` leaves the finished checkpoint behind but does not make the import fail.

Maintainer unit witness on current master:

```bash
go test ./lightning/pkg/importinto -run '^TestImporterRun$' -count=1
```

One test case proves:

- schemas/tables creation succeeds
- precheck succeeds
- `SubmitAndWait` succeeds
- `cpMgr.Remove(ctx, "all")` returns `checkpoint cleanup failed`
- `Importer.Run()` still returns `nil`

The retained-finished-checkpoint consequence is already covered by the current target matrix in the same package:

- same table name
- new nonempty input
- finished checkpoint reused
- result `nil`
- submissions `0`

### 2. What did you expect to see? (Required)

If checkpoint cleanup fails after a successful run, the import must fail explicitly, or at least mark the checkpoint state unusable for future runs.

A later run with different current input must either submit a new IMPORT job or return an explicit checkpoint lineage error. It must not silently skip all current input.

### 3. What did you see instead (Required)

Current code returns success even when the checkpoint cleanup step fails.

That leaves a finished checkpoint keyed only by table name. On the next run for the same table with a different nonempty input, the import backend returns success without submitting any job.

In the validated RED:

- current input is nonempty and distinct
- `SubmitAndWait` returns `nil`
- submission count is `0`

So a common cleanup fault can turn a later ordinary rerun into a silent full-input skip.

#### Likely root cause

`Importer.Run()` treats checkpoint removal as best-effort cleanup:

- success is already decided by `SubmitAndWait`
- `cpMgr.Remove()` failure is downgraded to a warning
- the finished checkpoint state remains durable

But the checkpoint lineage model only binds table name, job id, status, and group key. It does not bind the current input file/config/target fingerprint strongly enough to reject the next run.

So a common cleanup failure after run A leaves exactly the same finished checkpoint state that run B later interprets as proof that the new input has already finished.

### 4. What is your TiDB version? (Required)

Current source and unit matrix are on:

```text
TiDB: 13282a8bd06bd33324a4dbfd3c1c03685f3cd9aa
```

The retained-finished-checkpoint RED and no-checkpoint GREEN were validated in `lightning/pkg/importinto` on the same current source tree.

### Impact

This does not require a rare SQL function or a special non-default SQL mode.

The trigger chain is operationally ordinary:

1. an import finishes successfully,
2. checkpoint cleanup fails because of a common DB/storage/network problem,
3. the operator reruns the import for the same table with current nonempty input,
4. the backend returns success without submitting any import job.

The result is silent omission of the entire current input.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.