@@ -875,6 +875,7 @@ func GetLocalityInfo(
875875 return info , nil
876876}
877877
878+ // TODO (kev-cao): Remove in v26.2
878879type BackupTreeEntry struct {
879880 // The URI to the backup manifest.
880881 Uri string
@@ -884,11 +885,45 @@ type BackupTreeEntry struct {
884885 LocalityInfo jobspb.RestoreDetails_BackupLocalityInfo
885886}
886887
887- // zipBackupTreeEntries zips the default URIs, main backup manifests, and
888+ func (b BackupTreeEntry ) Start () hlc.Timestamp {
889+ return b .Manifest .StartTime
890+ }
891+
892+ func (b BackupTreeEntry ) End () hlc.Timestamp {
893+ return b .Manifest .EndTime
894+ }
895+
896+ func (b BackupTreeEntry ) Compacted () bool {
897+ return b .Manifest .IsCompacted
898+ }
899+
900+ func (b BackupTreeEntry ) MVCC () backuppb.MVCCFilter {
901+ return b .Manifest .MVCCFilter
902+ }
903+
904+ func (b BackupTreeEntry ) RevisionStart () hlc.Timestamp {
905+ return b .Manifest .RevisionStartTime
906+ }
907+
908+ // This interface is used as a stop-gap during the transitional period where
909+ // some restorable backups will not have backup indexes and some will.
910+ // Afterwards, we can remove this interface and replace instances of
911+ // ManifestLike with BackupIndexMetadata.
912+ // TODO (kev-cao): Remove in v26.2
913+ type ManifestLike interface {
914+ Start () hlc.Timestamp
915+ End () hlc.Timestamp
916+ Compacted () bool
917+ MVCC () backuppb.MVCCFilter
918+ RevisionStart () hlc.Timestamp
919+ }
920+
921+ // ZipBackupTreeEntries zips the default URIs, main backup manifests, and
888922// locality information into a slice of backupChainEntry structs. It assumes
889923// that the passed in slices are of the same length and that each entry at the
890924// same index in each slice corresponds to the same backup.
891- func zipBackupTreeEntries (
925+ // TODO (kev-cao): Remove in v26.2
926+ func ZipBackupTreeEntries (
892927 defaultURIs []string ,
893928 mainBackupManifests []backuppb.BackupManifest ,
894929 localityInfo []jobspb.RestoreDetails_BackupLocalityInfo ,
@@ -912,10 +947,11 @@ func zipBackupTreeEntries(
912947 return entries , nil
913948}
914949
915- // unzipBackupTreeEntries unzips a slice of backupChainEntry structs into
950+ // UnzipBackupTreeEntries unzips a slice of backupChainEntry structs into
916951// slices of their constituent parts: default URIs, main backup manifests, and
917952// locality info and returns the individual slices.
918- func unzipBackupTreeEntries (
953+ // TODO (kev-cao): Remove in v26.2
954+ func UnzipBackupTreeEntries (
919955 entries []BackupTreeEntry ,
920956) ([]string , []backuppb.BackupManifest , []jobspb.RestoreDetails_BackupLocalityInfo ) {
921957 defaultURIs := make ([]string , len (entries ))
@@ -935,85 +971,70 @@ func unzipBackupTreeEntries(
935971// The method also performs additional sanity checks to ensure the backups cover
936972// the requested time.
937973//
938- // TODO (kev-cao): Refactor this function to accept/return the BackupTreeEntry
939- // type and update surrounding caller logic to use it. This will allow us to
940- // refactor out the zipping and unzipping logic.
941- func ValidateEndTimeAndTruncate (
942- defaultURIs []string ,
943- mainBackupManifests []backuppb.BackupManifest ,
944- localityInfo []jobspb.RestoreDetails_BackupLocalityInfo ,
945- endTime hlc.Timestamp ,
946- includeSkipped bool ,
947- includeCompacted bool ,
948- ) ([]string , []backuppb.BackupManifest , []jobspb.RestoreDetails_BackupLocalityInfo , error ) {
949- backupEntries , err := zipBackupTreeEntries (defaultURIs , mainBackupManifests , localityInfo )
950- if err != nil {
951- return nil , nil , nil , err
952- }
953-
974+ // Note: This function assumes that the manifests in the chain are sorted by end
975+ // time in ascending order and ties are broken by start time in ascending order.
976+ func ValidateEndTimeAndTruncate [E ManifestLike ](
977+ manifests []E , endTime hlc.Timestamp , includeSkipped bool , includeCompacted bool ,
978+ ) ([]E , error ) {
954979 if ! includeCompacted {
955- backupEntries = skipCompactedBackups (backupEntries )
980+ manifests = skipCompactedBackups (manifests )
956981 }
957982
958983 if endTime .IsEmpty () {
959984 if includeSkipped {
960- uris , manifests , locality := unzipBackupTreeEntries (backupEntries )
961- return uris , manifests , locality , nil
985+ return manifests , nil
962986 }
963- backupEntries = elideSkippedLayers (backupEntries )
964- if err := validateContinuity (backupEntries ); err != nil {
965- return nil , nil , nil , err
987+ manifests = elideSkippedLayers (manifests )
988+ if err := validateContinuity (manifests ); err != nil {
989+ return nil , err
966990 }
967- uris , manifests , locality := unzipBackupTreeEntries (backupEntries )
968- return uris , manifests , locality , nil
991+ return manifests , nil
969992 }
970- for i , b := range backupEntries {
993+ for i , b := range manifests {
971994 // Find the backup that covers the requested time.
972- if ! (b .Manifest . StartTime . Less (endTime ) && endTime .LessEq (b .Manifest . EndTime )) {
995+ if ! (b .Start (). Less (endTime ) && endTime .LessEq (b .End () )) {
973996 continue
974997 }
975998
976999 // Ensure that the backup actually has revision history.
977- if ! endTime .Equal (b .Manifest . EndTime ) {
978- if b .Manifest . MVCCFilter != backuppb .MVCCFilter_All {
1000+ if ! endTime .Equal (b .End () ) {
1001+ if b .MVCC () != backuppb .MVCCFilter_All {
9791002 const errPrefix = "invalid RESTORE timestamp: restoring to arbitrary time requires that BACKUP for requested time be created with 'revision_history' option."
9801003 if i == 0 {
981- return nil , nil , nil , errors .Errorf (
1004+ return nil , errors .Errorf (
9821005 errPrefix + " nearest backup time is %s" ,
983- timeutil .Unix (0 , b .Manifest . EndTime .WallTime ).UTC (),
1006+ timeutil .Unix (0 , b .End () .WallTime ).UTC (),
9841007 )
9851008 }
986- return nil , nil , nil , errors .Errorf (
1009+ return nil , errors .Errorf (
9871010 errPrefix + " nearest BACKUP times are %s or %s" ,
988- timeutil .Unix (0 , mainBackupManifests [i - 1 ].EndTime .WallTime ).UTC (),
989- timeutil .Unix (0 , b .Manifest . EndTime .WallTime ).UTC (),
1011+ timeutil .Unix (0 , manifests [i - 1 ].End () .WallTime ).UTC (),
1012+ timeutil .Unix (0 , b .End () .WallTime ).UTC (),
9901013 )
9911014 }
9921015 // Ensure that the revision history actually covers the requested time -
9931016 // while the BACKUP's start and end might contain the requested time for
9941017 // example if start time is 0 (full backup), the revision history was
9951018 // only captured since the GC window. Note that the RevisionStartTime is
9961019 // the latest for ranges backed up.
997- if endTime .LessEq (b .Manifest . RevisionStartTime ) {
998- return nil , nil , nil , errors .Errorf (
1020+ if endTime .LessEq (b .RevisionStart () ) {
1021+ return nil , errors .Errorf (
9991022 "invalid RESTORE timestamp: BACKUP for requested time only has revision history" +
1000- " from %v" , timeutil .Unix (0 , b .Manifest . RevisionStartTime .WallTime ).UTC (),
1023+ " from %v" , timeutil .Unix (0 , b .RevisionStart () .WallTime ).UTC (),
10011024 )
10021025 }
10031026 }
10041027 if includeSkipped {
1005- uris , manifests , locality := unzipBackupTreeEntries (backupEntries [:i + 1 ])
1006- return uris , manifests , locality , nil
1028+ return manifests [:i + 1 ], nil
10071029 }
1008- backupEntries = elideSkippedLayers (backupEntries [:i + 1 ])
1009- if err := validateContinuity (backupEntries ); err != nil {
1010- return nil , nil , nil , err
1030+ manifests = elideSkippedLayers (manifests [:i + 1 ])
1031+ if err := validateContinuity (manifests ); err != nil {
1032+ return nil , err
10111033 }
1012- uris , manifests , locality := unzipBackupTreeEntries (backupEntries )
1013- return uris , manifests , locality , nil
1034+ return manifests , nil
10141035 }
10151036
1016- return nil , nil , nil , errors .Errorf (
1037+ return nil , errors .Errorf (
10171038 "invalid RESTORE timestamp: supplied backups do not cover requested time" ,
10181039 )
10191040}
@@ -1022,26 +1043,30 @@ func ValidateEndTimeAndTruncate(
10221043// backups and returns the updated list backup entries.
10231044//
10241045// NOTE: This function modifies the underlying memory of the slices passed in.
1025- func skipCompactedBackups ( backupEntries []BackupTreeEntry ) []BackupTreeEntry {
1026- for i := len (backupEntries ) - 1 ; i >= 0 ; i -- {
1027- if backupEntries [i ].Manifest . IsCompacted {
1028- backupEntries = slices .Delete (backupEntries , i , i + 1 )
1046+ func skipCompactedBackups [ E ManifestLike ]( manifests []E ) []E {
1047+ for i := len (manifests ) - 1 ; i >= 0 ; i -- {
1048+ if manifests [i ].Compacted () {
1049+ manifests = slices .Delete (manifests , i , i + 1 )
10291050 }
10301051 }
1031- return backupEntries
1052+ return manifests
10321053}
10331054
10341055// validateContinuity checks that the backups are continuous.
1035- func validateContinuity (backupEntries []BackupTreeEntry ) error {
1036- if len (backupEntries ) == 0 {
1056+ //
1057+ // Note: This fucntion assumes the backups are sorted by end time in ascending
1058+ // order.
1059+ func validateContinuity [E ManifestLike ](manifests []E ) error {
1060+ if len (manifests ) == 0 {
10371061 return errors .AssertionFailedf ("an empty chain of backups cannot cover an end time" )
10381062 }
1039- for i := range len (backupEntries ) - 1 {
1040- if ! backupEntries [i ].Manifest .EndTime .Equal (backupEntries [i + 1 ].Manifest .StartTime ) {
1063+ for i := range len (manifests ) - 1 {
1064+ end := manifests [i ].End ()
1065+ if ! end .Equal (manifests [i + 1 ].Start ()) {
10411066 return errors .AssertionFailedf (
10421067 "backups are not continuous: %dth backup ends at %+v, %dth backup starts at %+v" ,
1043- i , backupEntries [i ].Manifest . EndTime ,
1044- i + 1 , backupEntries [i + 1 ].Manifest . StartTime ,
1068+ i , manifests [i ].End () ,
1069+ i + 1 , manifests [i + 1 ].Start () ,
10451070 )
10461071 }
10471072 }
@@ -1051,49 +1076,53 @@ func validateContinuity(backupEntries []BackupTreeEntry) error {
10511076// elideSkippedLayers removes backups that are skipped in the backup chain and
10521077// ensures only backups that will be used in the restore are returned.
10531078//
1054- // Note: This assumes that the provided backups are sorted in increasing order
1055- // by end time, and then sorted in increasing order by start time to break ties .
1056- func elideSkippedLayers ( backupEntries []BackupTreeEntry ) []BackupTreeEntry {
1057- backupEntries = elideDuplicateEndTimes (backupEntries )
1058- i := len (backupEntries ) - 1
1079+ // Note: This function assumes that the manifests in the chain are sorted by end
1080+ // time in ascending order and ties are broken by start time in ascending order .
1081+ func elideSkippedLayers [ E ManifestLike ]( manifests []E ) []E {
1082+ manifests = elideDuplicateEndTimes (manifests )
1083+ i := len (manifests ) - 1
10591084 for i > 0 {
10601085 // Find j such that backups[j] is parent of backups[i].
10611086 j := i - 1
1062- for j >= 0 && ! backupEntries [i ].Manifest .StartTime .Equal (backupEntries [j ].Manifest .EndTime ) {
1087+ start := manifests [i ].Start ()
1088+ for j >= 0 && ! start .Equal (manifests [j ].End ()) {
10631089 j --
10641090 }
10651091 // If there are backups between i and j, remove them.
10661092 // If j is less than 0, then no parent was found so nothing to skip.
10671093 if j != i - 1 && j >= 0 {
1068- backupEntries = slices .Delete (backupEntries , j + 1 , i )
1094+ manifests = slices .Delete (manifests , j + 1 , i )
10691095 }
10701096 // Move up to check the chain from j now.
10711097 i = j
10721098 }
1073- return backupEntries
1099+ return manifests
10741100}
10751101
10761102// elideDuplicateEndTimes ensures that backups in a list of backups are
10771103// functionally unique by removing any duplicates that have the same end time,
10781104// choosing backups with earlier start times and eliding the rest.
10791105//
1080- // Note: This assumes that the provided backups are sorted in increasing order
1081- // by end time, and then sorted in increasing order by start time to break ties.
1082- func elideDuplicateEndTimes (backupEntries []BackupTreeEntry ) []BackupTreeEntry {
1083- for i := range len (backupEntries ) - 1 {
1106+ // Note: This function assumes that the manifests in the chain are sorted by end
1107+ // time in ascending order and ties are broken by start time in ascending order.
1108+ func elideDuplicateEndTimes [E ManifestLike ](manifests []E ) []E {
1109+ for i := range len (manifests ) - 1 {
1110+ if i >= len (manifests ) {
1111+ break
1112+ }
10841113 j := i + 1
10851114 // Find j such that backups[j] no longer shares the same end time as
10861115 // backups[i].
1087- for j < len ( backupEntries ) &&
1088- backupEntries [ i ]. Manifest . EndTime . Equal (backupEntries [j ].Manifest . EndTime ) {
1116+ end := manifests [ i ]. End ()
1117+ for j < len ( manifests ) && end . Equal (manifests [j ].End () ) {
10891118 j ++
10901119 }
10911120 // If there exists backups between i and j, remove them.
10921121 if j > i + 1 {
1093- backupEntries = slices .Delete (backupEntries , i + 1 , j )
1122+ manifests = slices .Delete (manifests , i + 1 , j )
10941123 }
10951124 }
1096- return backupEntries
1125+ return manifests
10971126}
10981127
10991128// GetBackupIndexAtTime returns the index of the latest backup in
0 commit comments