October 24, 2022·4 min read·engineeringcockroachdbbackuprecovery

CockroachDB incremental restore could resurrect rolled-back import rows

Advisory A-88042 concerns rows restored from an import that later failed. Non-MVCC rollback and backup-chain reconstruction must agree about which older data remains eligible for restoration.

A backup chain must preserve the effects of removing data as well as adding it. That becomes difficult when an import rollback erases physical data without recording ordinary versioned deletions. A later incremental backup can describe the current table while older files still contain imported rows that should no longer belong to it.

The October 24 advisory A-88042 describes an incremental restore bringing back those unwanted rows. It affects CockroachDB 22.1.0 through 22.1.8 and 21.2.0 through 21.2.16. The stated maintenance fixes are 22.1.9 and 21.2.17. Exposure depends on a particular sequence involving a backup taken during an import that later fails or is canceled.

I value CockroachDB's consistency model, but recovery has to reconstruct the intended history too. A table being correct after rollback does not establish that combining its backup files will reproduce the same state. The advisory makes that distinction concrete.

The backup captures an unfinished import

The sequence begins with IMPORT INTO writing to an offline table. A backup, typically a full-cluster backup, includes rows written by that in-progress job. Those rows are not necessarily part of a successfully completed import. The backup has captured an intermediate state that later recovery must interpret correctly.

Next, the import fails or is canceled. Its rollback reverts the rows it wrote and brings the table online again. An incremental backup then appends to the chain that began while the import was running. Restoring that chain through the affected implementation can include the reverted rows.

The ordering is essential. An import followed by an unrelated backup does not automatically satisfy these conditions. The older backup must contain the unfinished import's data, and the later chain must cross the rollback. The advisory says a chain beginning with a full backup after cancellation is unaffected.

That qualification identifies a meaningful boundary. A new full backup captures the table after rollback without relying on the earlier captured import data. The problematic reconstruction instead combines a base that contains those rows with later information that must supersede them.

Rollback does not use ordinary versioned deletion

The technical issue 88043 describes import rollback using a non-MVCC clear-range operation. The physical removal does not create the same versioned record as a conventional row deletion. An incremental backup cannot assume that merely collecting later versions will describe the removal.

The issue explains that cluster backups included offline importing tables despite an assumption that they did not. That mismatch matters because the earlier backup can contain data from a job that never commits successfully. Recovery must either exclude that intermediate data or recognize that a later representation replaces it.

This is a mismatch between backup semantics and a bulk-operation implementation. The live table can be back online with the import's additions removed, while the earlier backup still contains those additions. Neither reading the live table nor listing successful backup jobs establishes what a restore will do with that older material.

The related issue 88042 describes additional complexity for revision-history backups. Offline descriptors can enter the history, and the logic deciding which table spans to reintroduce must account for them. Those details explain why a single assumption about excluding offline tables was insufficient across all backup forms.

Restoration needs to recognize reintroduced spans

The restore repair in pull request 87312, merged September 22, describes using backup manifests to determine the last introduction of a span. Data from backups ending before that point must not be used to reconstruct that span. The newer reintroduced representation supersedes the older material.

The patch's explanation connects this to non-MVCC operations. When a span is backed up again from its beginning, restore needs to avoid ingesting older files whose contents the operation invalidated. Otherwise the restoration process combines representations that should not be cumulative.

This gives the repair a specific role. It changes how the restore coordinator selects eligible backed-up data. The decision is not simply whether every file is readable. A readable file can contain rows that no longer belong to the requested recovered state.

I find that distinction important because backup integrity has several meanings. A complete set of files and a successful transfer do not establish that every older row version remains appropriate input to restoration. The chain's introduction metadata determines which representation should supply the span.

Mitigation follows the chain and the restore version

The advisory recommends updating to the fixed maintenance versions. If restoration must use older software, it recommends restoring only full backups where possible. If an incremental restore is unavoidable, its guidance limits restoration to tables where IMPORT INTO did not run or completed successfully.

That is a targeted workaround tied to the failure sequence. A canceled import and a successful import have different consequences for whether the earlier imported data remains valid. The advisory's broad recommendation is still to update, rather than treating an incomplete operational history as proof that a chain avoids the issue.

The source does not describe repairing the problem by replaying the same affected restore repeatedly. Repeating a deterministic selection of inappropriate older files can reproduce the same unwanted rows. The change must address the software interpreting the chain or the set of backups and tables selected.

The reported impact is extra rows in the restored table, not merely an explicit recovery failure. That makes validating a recovered application's data more involved than checking whether the restore job completed. The relevant question is whether the rows represent the intended state after the failed import's rollback, including the absence of rows that the failed job had temporarily written.

A full backup beginning after cancellation avoids the affected historical combination described by the advisory. For an existing chain crossing that event, the maintenance fix changes how restoration handles older span data. Those are concrete ways to prevent unfinished import rows from becoming part of the recovered database.