pingcap / pingcap/tidb

[DDL] FLASHBACK DATABASE can reset recovered sequence values and reuse existing IDs

Open
#69,781 2 comments 0 reactions 0 assignees View on GitHub
affects-7.5 affects-8.1 affects-8.5 component/ddl found-by-ai severity/major type/bug
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

# FLASHBACK DATABASE can reset recovered sequence values and reuse existing IDs

## Bug Report

### 1. Minimal reproduce step (Required)

The following reproduces the issue without failpoints:

```sql
DROP DATABASE IF EXISTS ai_native_seq_fb;
CREATE DATABASE ai_native_seq_fb;
USE ai_native_seq_fb;

CREATE SEQUENCE seq START WITH 1 INCREMENT BY 1 CACHE 100;
CREATE TABLE t (
id BIGINT PRIMARY KEY DEFAULT NEXT VALUE FOR seq,
note VARCHAR(64)
);

INSERT INTO t(note) VALUES ('before_1'), ('before_2');
SELECT * FROM t ORDER BY id;
SELECT NEXTVAL(seq) AS before_drop_nextval;

DROP DATABASE ai_native_seq_fb;
FLASHBACK DATABASE ai_native_seq_fb;
USE ai_native_seq_fb;

SELECT * FROM t ORDER BY id;
SELECT NEXTVAL(seq) AS after_flashback_nextval;
INSERT INTO t(note) VALUES ('after_flashback_default_insert');
```

Observed result:

```text
rows before DROP DATABASE: (1, before_1), (2, before_2)
before_drop_nextval: 3
rows after FLASHBACK DATABASE: (1, before_1), (2, before_2)
after_flashback_nextval: 1
following default INSERT: ERROR 1062 Duplicate entry '2' for key 't.PRIMARY'
```

The sequence object is present after recovery, but its allocation state has moved behind values
that already exist in the recovered table.

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

`FLASHBACK DATABASE` should restore enough sequence state that a recovered sequence cannot allocate
values already materialized before the database was dropped. The post-recovery `NEXTVAL(seq)`
should continue beyond the recovered sequence state, and the following sequence-backed default
insert should succeed without a duplicate key.

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

`FLASHBACK DATABASE` restores the sequence metadata and the table rows, but the sequence runtime
value is reset. `NEXTVAL(seq)` returns 1 even though IDs 1 and 2 already exist. The next default
insert then allocates 2 and fails with `ERROR 1062`.

This is behavioral state loss rather than only a `SHOW CREATE SEQUENCE` metadata difference. An
application using the recovered sequence as an identifier generator observes the generator moving
backward and reusing existing IDs.

### 4. Controls

The following controls were run on the same build:

- The same sequence and default-column path without database recovery produces IDs 1 and 2,
`NEXTVAL(seq)=3`, and a following default insert succeeds with ID 4.
- An `AUTO_INCREMENT` table containing IDs 1 and 2 was recovered with `FLASHBACK DATABASE`; its
following default insert succeeded with a non-colliding ID (30001 on the testbed).

These controls isolate the failure to recovery of sequence-specific runtime state, rather than the
default expression or generic database recovery path.

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

Reproduced without failpoints on an authorized test cluster:

```text
Release Version: v9.0.0-beta.2.pre-1895-g5c9198e948
Git Commit Hash: 5c9198e9484db852b8477ce0014e0422ff9ec6a9
Server string: 8.0.11-TiDB-v9.0.0-beta.2.pre-1895-g5c9198e948
```

### Suspected root cause

The schema recovery path enumerates historical objects through `ListTables` and routes each
`TableInfo`, including sequences, through the generic `recoverTable` path.

`recoverTable` calls `CreateTableAndSetAutoID`, which restores ordinary row ID, auto-increment, and
auto-random state. Sequence allocation state is stored separately under sequence-specific value
and cycle keys, and normal sequence creation uses `CreateSequenceAndSetSeqValue`. The generic table
recovery path does not rebuild those sequence keys.

The missing proof obligation is:

```text
recovering the historical TableInfo
does not imply
recovering all runtime state required by that object kind
```

A recovery test should verify post-recovery allocation behavior, not only that the sequence object
exists: `NEXTVAL` must not reuse existing values, and a sequence-backed default insert must remain
valid.

Found by AI-assisted testing.

Contributor guide

Open the contributing guide

Research direction

Start by reading the ListTables and recoverTable recovery paths, then compare them with CreateSequenceAndSetSeqValue and the sequence-specific value and cycle keys. Run the provided FLASHBACK DATABASE reproduction and add recovery coverage showing that NEXTVAL advances beyond recovered IDs and a sequence-backed default insert avoids duplicate keys.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.