Skip to content

Commit 46fb55e

Browse files
authored
Initial support for partitioned backup (#2332)
* Initial support for partitioned backup
1 parent be55290 commit 46fb55e

20 files changed

+556
-166
lines changed

api/v1beta2/foundationdb_process_class.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import (
2727
// ProcessClass models the class of a pod
2828
type ProcessClass string
2929

30+
// See https://github.com/apple/foundationdb/blob/release-7.1/fdbrpc/Locality.cpp for additional information regarding the
31+
// process classes and process roles.
3032
const (
3133
// ProcessClassStorage model for FDB class storage
3234
ProcessClassStorage ProcessClass = "storage"
@@ -50,6 +52,8 @@ const (
5052
ProcessClassCommitProxy ProcessClass = "commit_proxy"
5153
// ProcessClassGrvProxy model for FDB grv_proxy processes
5254
ProcessClassGrvProxy ProcessClass = "grv_proxy"
55+
// ProcessClassBackup model for FDB backup processes
56+
ProcessClassBackup ProcessClass = "backup"
5357
)
5458

5559
// IsStateful determines whether a process class should store data.

api/v1beta2/foundationdbbackup_types.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,30 @@ type FoundationDBBackupSpec struct {
117117
// +kubebuilder:validation:Enum=split;unified
118118
// +kubebuilder:default:=split
119119
ImageType *ImageType `json:"imageType,omitempty"`
120+
121+
// BackupType defines the backup type that should be used for the backup. When the BackupType is set to
122+
// BackupTypePartitionedLog, it's expected that the FoundationDBCluster creates and manages the additional
123+
// backup worker processes. A migration to a different backup type is not yet supported in the operator.
124+
// Default: "backup_agent".
125+
// +kubebuilder:validation:Optional
126+
// +kubebuilder:validation:Enum=backup_agent;partitioned_log
127+
// +kubebuilder:default:=backup_agent
128+
BackupType *BackupType `json:"backupType,omitempty"`
120129
}
121130

131+
// BackupType defines the backup type that should be used for the backup.
132+
// +kubebuilder:validation:MaxLength=64
133+
type BackupType string
134+
135+
const (
136+
// BackupTypeDefault refers to the current default backup type with additional backup agents.
137+
BackupTypeDefault BackupType = "backup_agent"
138+
139+
// BackupTypePartitionedLog refers to the new partitioned log backup system, see
140+
// https://github.com/apple/foundationdb/blob/main/design/backup_v2_partitioned_logs.md.
141+
BackupTypePartitionedLog BackupType = "partitioned_log"
142+
)
143+
122144
// FoundationDBBackupStatus describes the current status of the backup for a cluster.
123145
type FoundationDBBackupStatus struct {
124146
// AgentCount provides the number of agents that are up-to-date, ready,
@@ -279,6 +301,18 @@ type FoundationDBLiveBackupStatus struct {
279301
type FoundationDBLiveBackupStatusState struct {
280302
// Running determines whether the backup is currently running.
281303
Running bool `json:"Running,omitempty"`
304+
305+
// Restorable if true, the backup can be restored
306+
Restorable *bool `json:"Restorable,omitempty"`
307+
308+
// LatestRestorablePoint contains information about the latest restorable point if any exists.
309+
LatestRestorablePoint *LatestRestorablePoint `json:"LatestRestorablePoint,omitempty"`
310+
}
311+
312+
// LatestRestorablePoint contains information about the latest restorable point if any exists.
313+
type LatestRestorablePoint struct {
314+
// Version is the version that can be restored to.
315+
Version *uint64 `json:"Version,omitempty"`
282316
}
283317

284318
// GetDesiredAgentCount determines how many backup agents we should run
@@ -331,11 +365,34 @@ func (backup *FoundationDBBackup) CheckReconciliation() (bool, error) {
331365
return reconciled, nil
332366
}
333367

368+
// GetBackupType returns the backup type for the backup.
369+
func (backup *FoundationDBBackup) GetBackupType() BackupType {
370+
if backup.Spec.BackupType != nil {
371+
return *backup.Spec.BackupType
372+
}
373+
374+
return BackupTypeDefault
375+
}
376+
334377
// GetAllowTagOverride returns the bool value for AllowTagOverride
335378
func (foundationDBBackupSpec *FoundationDBBackupSpec) GetAllowTagOverride() bool {
336379
return ptr.Deref(foundationDBBackupSpec.AllowTagOverride, false)
337380
}
338381

382+
// GetEncryptionKey returns the encryption key path if the current version supports encrypted backups.
383+
func (backup *FoundationDBBackup) GetEncryptionKey() (string, error) {
384+
fdbVersion, err := ParseFdbVersion(backup.Spec.Version)
385+
if err != nil {
386+
return "", err
387+
}
388+
389+
if !fdbVersion.SupportsBackupEncryption() {
390+
return "", nil
391+
}
392+
393+
return backup.Spec.EncryptionKeyPath, nil
394+
}
395+
339396
// UseUnifiedImage returns true if the unified image should be used.
340397
func (backup *FoundationDBBackup) UseUnifiedImage() bool {
341398
imageType := ImageTypeSplit

api/v1beta2/zz_generated.deepcopy.go

Lines changed: 36 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/apps.foundationdb.org_foundationdbbackups.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ spec:
7171
- Stopped
7272
- Paused
7373
type: string
74+
backupType:
75+
default: backup_agent
76+
enum:
77+
- backup_agent
78+
- partitioned_log
79+
maxLength: 64
80+
type: string
7481
blobStoreConfiguration:
7582
properties:
7683
accountName:

controllers/admin_client_test.go

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,21 @@ var _ = Describe("admin_client_test", func() {
188188
})
189189
})
190190

191-
Context("with a backup running", func() {
191+
When("a backup running is running", func() {
192+
var backup *fdbv1beta2.FoundationDBBackup
193+
192194
BeforeEach(func() {
193-
err = mockAdminClient.StartBackup(
194-
"blobstore://test@test-service/test-backup",
195-
10,
196-
"",
197-
)
198-
Expect(err).NotTo(HaveOccurred())
195+
backup = &fdbv1beta2.FoundationDBBackup{
196+
Spec: fdbv1beta2.FoundationDBBackupSpec{
197+
BlobStoreConfiguration: &fdbv1beta2.BlobStoreConfiguration{
198+
BackupName: "test-backup",
199+
AccountName: "test",
200+
},
201+
SnapshotPeriodSeconds: ptr.To(10),
202+
},
203+
}
204+
205+
Expect(mockAdminClient.StartBackup(backup)).To(Succeed())
199206
})
200207

201208
It("should put the backup in the layer status", func() {
@@ -204,7 +211,7 @@ var _ = Describe("admin_client_test", func() {
204211
status.Cluster.Layers.Backup.Tags,
205212
).To(Equal(map[string]fdbv1beta2.FoundationDBStatusBackupTag{
206213
"default": {
207-
CurrentContainer: "blobstore://test@test-service/test-backup",
214+
CurrentContainer: backup.BackupURL(),
208215
RunningBackup: ptr.To(true),
209216
Restorable: ptr.To(true),
210217
},
@@ -213,8 +220,7 @@ var _ = Describe("admin_client_test", func() {
213220

214221
Context("with a paused backup", func() {
215222
BeforeEach(func() {
216-
err = mockAdminClient.PauseBackups()
217-
Expect(err).NotTo(HaveOccurred())
223+
Expect(mockAdminClient.PauseBackups()).To(Succeed())
218224
})
219225

220226
It("should mark the backup as paused", func() {
@@ -224,10 +230,8 @@ var _ = Describe("admin_client_test", func() {
224230

225231
Context("with an resume backup", func() {
226232
BeforeEach(func() {
227-
err = mockAdminClient.PauseBackups()
228-
Expect(err).NotTo(HaveOccurred())
229-
err = mockAdminClient.ResumeBackups()
230-
Expect(err).NotTo(HaveOccurred())
233+
Expect(mockAdminClient.PauseBackups()).To(Succeed())
234+
Expect(mockAdminClient.ResumeBackups()).To(Succeed())
231235
})
232236

233237
It("should mark the backup as not paused", func() {
@@ -237,16 +241,15 @@ var _ = Describe("admin_client_test", func() {
237241

238242
Context("with a stopped backup", func() {
239243
BeforeEach(func() {
240-
err = mockAdminClient.StopBackup("blobstore://test@test-service/test-backup")
241-
Expect(err).NotTo(HaveOccurred())
244+
Expect(mockAdminClient.StopBackup(backup)).To(Succeed())
242245
})
243246

244247
It("should mark the backup as stopped", func() {
245248
Expect(
246249
status.Cluster.Layers.Backup.Tags,
247250
).To(Equal(map[string]fdbv1beta2.FoundationDBStatusBackupTag{
248251
"default": {
249-
CurrentContainer: "blobstore://test@test-service/test-backup",
252+
CurrentContainer: backup.BackupURL(),
250253
RunningBackup: ptr.To(false),
251254
Restorable: ptr.To(true),
252255
},
@@ -258,6 +261,8 @@ var _ = Describe("admin_client_test", func() {
258261

259262
Describe("backup status", func() {
260263
var status *fdbv1beta2.FoundationDBLiveBackupStatus
264+
var backup *fdbv1beta2.FoundationDBBackup
265+
261266
JustBeforeEach(func() {
262267
status, err = mockAdminClient.GetBackupStatus()
263268
Expect(err).NotTo(HaveOccurred())
@@ -273,16 +278,21 @@ var _ = Describe("admin_client_test", func() {
273278

274279
Context("with a backup running", func() {
275280
BeforeEach(func() {
276-
err = mockAdminClient.StartBackup(
277-
"blobstore://test@test-service/test-backup",
278-
10,
279-
"",
280-
)
281-
Expect(err).NotTo(HaveOccurred())
281+
backup = &fdbv1beta2.FoundationDBBackup{
282+
Spec: fdbv1beta2.FoundationDBBackupSpec{
283+
BlobStoreConfiguration: &fdbv1beta2.BlobStoreConfiguration{
284+
BackupName: "test-backup",
285+
AccountName: "test",
286+
},
287+
SnapshotPeriodSeconds: ptr.To(10),
288+
},
289+
}
290+
291+
Expect(mockAdminClient.StartBackup(backup)).To(Succeed())
282292
})
283293

284294
It("should put the backup in the status", func() {
285-
Expect(status.DestinationURL).To(Equal("blobstore://test@test-service/test-backup"))
295+
Expect(status.DestinationURL).To(Equal(backup.BackupURL()))
286296
Expect(status.Status.Running).To(BeTrue())
287297
Expect(status.BackupAgentsPaused).To(BeFalse())
288298
Expect(status.SnapshotIntervalSeconds).To(Equal(10))
@@ -301,10 +311,8 @@ var _ = Describe("admin_client_test", func() {
301311

302312
Context("with a resumed backup", func() {
303313
BeforeEach(func() {
304-
err = mockAdminClient.PauseBackups()
305-
Expect(err).NotTo(HaveOccurred())
306-
err = mockAdminClient.ResumeBackups()
307-
Expect(err).NotTo(HaveOccurred())
314+
Expect(mockAdminClient.PauseBackups()).To(Succeed())
315+
Expect(mockAdminClient.ResumeBackups()).To(Succeed())
308316
})
309317

310318
It("should mark the backup as not paused", func() {
@@ -314,8 +322,7 @@ var _ = Describe("admin_client_test", func() {
314322

315323
Context("with a stopped backup", func() {
316324
BeforeEach(func() {
317-
err = mockAdminClient.StopBackup("blobstore://test@test-service/test-backup")
318-
Expect(err).NotTo(HaveOccurred())
325+
Expect(mockAdminClient.StopBackup(backup)).To(Succeed())
319326
})
320327

321328
It("should mark the backup as stopped", func() {
@@ -325,12 +332,14 @@ var _ = Describe("admin_client_test", func() {
325332

326333
Context("with a modification to the snapshot time", func() {
327334
BeforeEach(func() {
328-
err = mockAdminClient.ModifyBackup(20)
329-
Expect(err).NotTo(HaveOccurred())
335+
backup.Spec.SnapshotPeriodSeconds = ptr.To(20)
336+
Expect(mockAdminClient.ModifyBackup(backup)).NotTo(HaveOccurred())
330337
})
331338

332339
It("should mark the backup as stopped", func() {
333-
Expect(status.SnapshotIntervalSeconds).To(Equal(20))
340+
Expect(
341+
status.SnapshotIntervalSeconds,
342+
).To(BeNumerically("==", backup.SnapshotPeriodSeconds()))
334343
})
335344
})
336345
})

controllers/modify_backup.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ func (s modifyBackup) reconcile(
4141
return nil
4242
}
4343

44-
snapshotPeriod := backup.SnapshotPeriodSeconds()
45-
if backup.Status.BackupDetails.SnapshotPeriodSeconds != snapshotPeriod {
44+
if backup.Status.BackupDetails.SnapshotPeriodSeconds != backup.SnapshotPeriodSeconds() {
4645
adminClient, err := r.adminClientForBackup(ctx, backup)
4746
if err != nil {
4847
return &requeue{curError: err}
@@ -51,7 +50,7 @@ func (s modifyBackup) reconcile(
5150
_ = adminClient.Close()
5251
}()
5352

54-
err = adminClient.ModifyBackup(snapshotPeriod)
53+
err = adminClient.ModifyBackup(backup)
5554
if err != nil {
5655
return &requeue{curError: err}
5756
}

controllers/start_backup.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,7 @@ func (s startBackup) reconcile(
4949
_ = adminClient.Close()
5050
}()
5151

52-
err = adminClient.StartBackup(
53-
backup.BackupURL(),
54-
backup.SnapshotPeriodSeconds(),
55-
backup.Spec.EncryptionKeyPath,
56-
)
52+
err = adminClient.StartBackup(backup)
5753
if err != nil {
5854
return &requeue{curError: err}
5955
}

controllers/stop_backup.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (s stopBackup) reconcile(
4949
_ = adminClient.Close()
5050
}()
5151

52-
err = adminClient.StopBackup(backup.BackupURL())
52+
err = adminClient.StopBackup(backup)
5353
if err != nil {
5454
return &requeue{curError: err}
5555
}

0 commit comments

Comments
 (0)