diff --git a/api/v1/objectstore_types.go b/api/v1/objectstore_types.go index 80c4742..c33f085 100644 --- a/api/v1/objectstore_types.go +++ b/api/v1/objectstore_types.go @@ -75,6 +75,12 @@ type RecoveryWindow struct { // The last successful backup time LastSuccessfulBackupTime *metav1.Time `json:"lastSuccussfulBackupTime,omitempty"` + + // The timestamp of the first WAL file successfully submitted to the object store + FirstWALSubmissionTime *metav1.Time `json:"firstWALSubmissionTime,omitempty"` + + // The timestamp of the last WAL file successfully submitted to the object store + LastWALSubmissionTime *metav1.Time `json:"lastWALSubmissionTime,omitempty"` } // +kubebuilder:object:root=true diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index 11fb2ae..f7b60c4 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -157,6 +157,14 @@ func (in *RecoveryWindow) DeepCopyInto(out *RecoveryWindow) { in, out := &in.LastSuccessfulBackupTime, &out.LastSuccessfulBackupTime *out = (*in).DeepCopy() } + if in.FirstWALSubmissionTime != nil { + in, out := &in.FirstWALSubmissionTime, &out.FirstWALSubmissionTime + *out = (*in).DeepCopy() + } + if in.LastWALSubmissionTime != nil { + in, out := &in.LastWALSubmissionTime, &out.LastWALSubmissionTime + *out = (*in).DeepCopy() + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RecoveryWindow. diff --git a/config/crd/bases/barmancloud.cnpg.io_objectstores.yaml b/config/crd/bases/barmancloud.cnpg.io_objectstores.yaml index 6fb87b5..7dfcddd 100644 --- a/config/crd/bases/barmancloud.cnpg.io_objectstores.yaml +++ b/config/crd/bases/barmancloud.cnpg.io_objectstores.yaml @@ -609,10 +609,20 @@ spec: restored. format: date-time type: string + firstWALSubmissionTime: + description: The timestamp of the first WAL file successfully + submitted to the object store + format: date-time + type: string lastSuccussfulBackupTime: description: The last successful backup time format: date-time type: string + lastWALSubmissionTime: + description: The timestamp of the last WAL file successfully + submitted to the object store + format: date-time + type: string type: object description: ServerRecoveryWindow maps each server to its recovery window diff --git a/internal/cnpgi/common/wal.go b/internal/cnpgi/common/wal.go index 0da8a39..ef8dfbc 100644 --- a/internal/cnpgi/common/wal.go +++ b/internal/cnpgi/common/wal.go @@ -17,6 +17,7 @@ import ( walUtils "github.com/cloudnative-pg/machinery/pkg/fileutils/wals" "github.com/cloudnative-pg/machinery/pkg/log" apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -184,6 +185,12 @@ func (w WALServiceImplementation) Archive( } } + // Update WAL submission timing in ObjectStore status after successful archiving + if err := w.updateWALSubmissionTime(ctx, &objectStore, configuration.ServerName); err != nil { + contextLogger.Error(err, "failed to update WAL submission time in ObjectStore status") + // Don't fail the archiving operation for status update errors + } + return &wal.WALArchiveResult{}, nil } @@ -469,3 +476,30 @@ func isEndOfWALStream(results []barmanRestorer.Result) bool { return false } + +// updateWALSubmissionTime updates the WAL submission timing in the ObjectStore status +func (w WALServiceImplementation) updateWALSubmissionTime( + ctx context.Context, + objectStore *barmancloudv1.ObjectStore, + serverName string, +) error { + now := metav1.NewTime(time.Now()) + + if objectStore.Status.ServerRecoveryWindow == nil { + objectStore.Status.ServerRecoveryWindow = make(map[string]barmancloudv1.RecoveryWindow) + } + + recoveryWindow := objectStore.Status.ServerRecoveryWindow[serverName] + + // Set first WAL submission time if not already set + if recoveryWindow.FirstWALSubmissionTime == nil { + recoveryWindow.FirstWALSubmissionTime = &now + } + + // Always update last WAL submission time + recoveryWindow.LastWALSubmissionTime = &now + + objectStore.Status.ServerRecoveryWindow[serverName] = recoveryWindow + + return w.Client.Status().Update(ctx, objectStore) +} diff --git a/internal/cnpgi/instance/retention.go b/internal/cnpgi/instance/retention.go index 6b68dfb..37f475e 100644 --- a/internal/cnpgi/instance/retention.go +++ b/internal/cnpgi/instance/retention.go @@ -176,14 +176,21 @@ func (c *CatalogMaintenanceRunnable) updateRecoveryWindow( return ptr.To(metav1.NewTime(*t)) } - recoveryWindow := barmancloudv1.RecoveryWindow{ - FirstRecoverabilityPoint: convertTime(backupList.GetFirstRecoverabilityPoint()), - LastSuccessfulBackupTime: convertTime(backupList.GetLastSuccessfulBackupTime()), - } - if objectStore.Status.ServerRecoveryWindow == nil { objectStore.Status.ServerRecoveryWindow = make(map[string]barmancloudv1.RecoveryWindow) } + + // get existing recovery window to preserve WAL submission timing + existingWindow := objectStore.Status.ServerRecoveryWindow[serverName] + + recoveryWindow := barmancloudv1.RecoveryWindow{ + FirstRecoverabilityPoint: convertTime(backupList.GetFirstRecoverabilityPoint()), + LastSuccessfulBackupTime: convertTime(backupList.GetLastSuccessfulBackupTime()), + // preserve existing WAL submission timing + FirstWALSubmissionTime: existingWindow.FirstWALSubmissionTime, + LastWALSubmissionTime: existingWindow.LastWALSubmissionTime, + } + objectStore.Status.ServerRecoveryWindow[serverName] = recoveryWindow return c.Client.Status().Update(ctx, objectStore) diff --git a/manifest.yaml b/manifest.yaml index c43ef4b..7dc9f0b 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -608,10 +608,20 @@ spec: restored. format: date-time type: string + firstWALSubmissionTime: + description: The timestamp of the first WAL file successfully + submitted to the object store + format: date-time + type: string lastSuccussfulBackupTime: description: The last successful backup time format: date-time type: string + lastWALSubmissionTime: + description: The timestamp of the last WAL file successfully + submitted to the object store + format: date-time + type: string type: object description: ServerRecoveryWindow maps each server to its recovery window