Skip to content

Commit 89fed9a

Browse files
sionsmithclaude
andcommitted
feat: add spec.backoffLimit; default restore jobs to a single attempt
The Jobs generated for KafkaRestore and KafkaBackup hardcoded backoffLimit: 3, so a failing restore made up to 4 pod attempts. Restores append to or purge target topics, which makes implicit retries of a partially completed attempt a data-duplication hazard — each run should be intentional. - Add spec.backoffLimit (minimum 0) to both CRDs, plumbed into the restore Job, one-shot backup Job, and scheduled CronJob template. - Restore Jobs now default to backoffLimit 0 (exactly one attempt); retries are opt-in. Backup Jobs keep the previous default of 3. Fixes #31 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent f75621f commit 89fed9a

14 files changed

Lines changed: 155 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
44

55
## 0.2.10 - 2026-06-12
66

7+
### Added
8+
9+
- Add `spec.backoffLimit` to `KafkaRestore` and `KafkaBackup` to control pod retries on generated Jobs (including scheduled CronJob runs). Fixes [#31](https://github.com/osodevops/strimzi-backup-operator/issues/31).
10+
11+
### Changed
12+
13+
- Restore Jobs now default to `backoffLimit: 0` (a single attempt, previously 3): restores append to or purge target topics, so retrying a partially completed attempt can duplicate data. Set `spec.backoffLimit` explicitly to opt into retries. Backup Jobs keep the previous default of 3.
14+
715
### Fixed
816

917
- Delete Jobs, CronJobs, and ConfigMaps with explicit Background propagation when a `KafkaBackup`/`KafkaRestore` is deleted. The batch/v1 Job API's legacy default deletion propagation is `Orphan`, which stripped the Job ownerReference from its pods and left Completed pods behind. Fixes [#30](https://github.com/osodevops/strimzi-backup-operator/issues/30).

deploy/crds/kafkabackups.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ spec:
9696
required:
9797
- type
9898
type: object
99+
backoffLimit:
100+
description: Number of pod retries before a backup Job is marked failed (`spec.backoffLimit` on generated Jobs, including scheduled CronJob runs). Defaults to 3.
101+
format: int32
102+
minimum: 0.0
103+
nullable: true
104+
type: integer
99105
backup:
100106
description: Backup options (compression, encryption, parallelism)
101107
nullable: true

deploy/crds/kafkarestores.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ spec:
9393
required:
9494
- type
9595
type: object
96+
backoffLimit:
97+
description: 'Number of pod retries before the restore Job is marked failed (`spec.backoffLimit` on the generated Job). Defaults to 0 so a restore runs exactly once: restores append to or purge target topics, so a retry of a partially completed attempt can duplicate data and must be opted into deliberately.'
98+
format: int32
99+
minimum: 0.0
100+
nullable: true
101+
type: integer
96102
backupRef:
97103
description: Reference to the source backup
98104
properties:

deploy/helm/strimzi-backup-operator/crds/kafkabackups.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ spec:
9696
required:
9797
- type
9898
type: object
99+
backoffLimit:
100+
description: Number of pod retries before a backup Job is marked failed (`spec.backoffLimit` on generated Jobs, including scheduled CronJob runs). Defaults to 3.
101+
format: int32
102+
minimum: 0.0
103+
nullable: true
104+
type: integer
99105
backup:
100106
description: Backup options (compression, encryption, parallelism)
101107
nullable: true

deploy/helm/strimzi-backup-operator/crds/kafkarestores.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ spec:
9393
required:
9494
- type
9595
type: object
96+
backoffLimit:
97+
description: 'Number of pod retries before the restore Job is marked failed (`spec.backoffLimit` on the generated Job). Defaults to 0 so a restore runs exactly once: restores append to or purge target topics, so a retry of a partially completed attempt can duplicate data and must be opted into deliberately.'
98+
format: int32
99+
minimum: 0.0
100+
nullable: true
101+
type: integer
96102
backupRef:
97103
description: Reference to the source backup
98104
properties:

src/adapters/backup_config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ mod tests {
507507
resources: None,
508508
template: None,
509509
image: None,
510+
backoff_limit: None,
510511
consumer_groups: None,
511512
};
512513
let mut backup = KafkaBackup::new("test-backup", spec);

src/crd/kafka_backup.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ pub struct KafkaBackupSpec {
8989
/// Container image for the backup job (default: osodevops/kafka-backup:v0.15.6)
9090
#[serde(skip_serializing_if = "Option::is_none")]
9191
pub image: Option<String>,
92+
93+
/// Number of pod retries before a backup Job is marked failed
94+
/// (`spec.backoffLimit` on generated Jobs, including scheduled CronJob
95+
/// runs). Defaults to 3.
96+
#[schemars(range(min = 0))]
97+
#[serde(skip_serializing_if = "Option::is_none")]
98+
pub backoff_limit: Option<i32>,
9299
}
93100

94101
/// Backup-specific options

src/crd/kafka_restore.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ pub struct KafkaRestoreSpec {
8585
/// Container image for the restore job (default: osodevops/kafka-backup:v0.15.6)
8686
#[serde(skip_serializing_if = "Option::is_none")]
8787
pub image: Option<String>,
88+
89+
/// Number of pod retries before the restore Job is marked failed
90+
/// (`spec.backoffLimit` on the generated Job). Defaults to 0 so a restore
91+
/// runs exactly once: restores append to or purge target topics, so a
92+
/// retry of a partially completed attempt can duplicate data and must be
93+
/// opted into deliberately.
94+
#[schemars(range(min = 0))]
95+
#[serde(skip_serializing_if = "Option::is_none")]
96+
pub backoff_limit: Option<i32>,
8897
}
8998

9099
/// Reference to a KafkaBackup CR and optional specific backup snapshot

src/jobs/backup_job.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ pub fn build_backup_job(
9999
..Default::default()
100100
},
101101
spec: Some(JobSpec {
102-
backoff_limit: Some(3),
102+
backoff_limit: Some(backup.spec.backoff_limit.unwrap_or(3)),
103103
template: PodTemplateSpec {
104104
metadata: Some(ObjectMeta {
105105
labels: Some(build_labels(&cr_name, &cluster.name, "backup")),
@@ -163,6 +163,7 @@ mod tests {
163163
resources: None,
164164
template: None,
165165
image: None,
166+
backoff_limit: None,
166167
};
167168

168169
let mut backup = KafkaBackup::new("test-backup", spec);

src/jobs/cronjob.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ pub fn build_backup_cronjob(
108108
..Default::default()
109109
}),
110110
spec: Some(JobSpec {
111-
backoff_limit: Some(3),
111+
backoff_limit: Some(backup.spec.backoff_limit.unwrap_or(3)),
112112
template: PodTemplateSpec {
113113
metadata: Some(ObjectMeta {
114114
labels: Some(labels),

0 commit comments

Comments
 (0)