Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bigtable): Hot backups #11215

Merged
merged 9 commits into from
Jan 8, 2025
Merged
146 changes: 136 additions & 10 deletions bigtable/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2150,9 +2150,60 @@ func (ac *AdminClient) RestoreTableFrom(ctx context.Context, sourceInstance, tab
return longrunning.InternalNewOperation(ac.lroClient, op).Wait(ctx, &resp)
}

type backupOptions struct {
bhshkh marked this conversation as resolved.
Show resolved Hide resolved
backupType *BackupType
hotToStandardTime *time.Time
}

// BackupOption can be used to specify parameters for backup operations.
type BackupOption interface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my main question here for the option style here is how complex you expect the options to become. I see only a single option with two behavior fields, so struct-based options that satisfy the apply() contract may be more than you need.

By way of comparison, if the contract is simplified to the core backupOptions and some funcs that can modify it:

type BackupOption func(*backupOptions)

func WithHotBackupOption(t time.Time) BackupOption {
  return func(bo *backupOptions) {
     bo.backupType = blah
     bo.hotToStandardTime = blah
  }
}

func WithExpiration(t time.Time) BackupOption {
  return func(bo *backupOptions) {
     bo.expireTime = blah
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. I have updated the code.

apply(o *backupOptions)
}

// setHotToStandardTime can be used to set backup type to [BackupTypeHot] and the
// time at which the hot backup will
// be converted to a standard backup.
// Once the `hot_to_standard_time` has passed, Cloud Bigtable will convert the
// hot backup to a standard backup. This value must be greater than the backup
// creation time by at least 24 hours.
type hotBackupOption struct {
bhshkh marked this conversation as resolved.
Show resolved Hide resolved
htsTime *time.Time
}

func (hbo hotBackupOption) apply(o *backupOptions) {
o.hotToStandardTime = hbo.htsTime
btHot := BackupTypeHot
o.backupType = &btHot
}

// HotToStandardBackup option can be used to create backup with
// type [BackupTypeHot] and specify time at which the hot backup will be
// converted to a standard backup. Once the `hot_to_standard_time` has passed,
// Cloud Bigtable will convert the hot backup to a standard backup.
// This value must be greater than the backup creation time by:
// - At least 24 hours
func HotToStandardBackup(hotToStandardTime time.Time) BackupOption {
return hotBackupOption{htsTime: &hotToStandardTime}
}

// HotBackup option can be used to create backup
// with type [BackupTypeHot]
func HotBackup() BackupOption {
return hotBackupOption{}
}

// CreateBackup creates a new backup in the specified cluster from the
// specified source table with the user-provided expire time.
func (ac *AdminClient) CreateBackup(ctx context.Context, table, cluster, backup string, expireTime time.Time) error {
return ac.createBackup(ctx, table, cluster, backup, expireTime, nil)
}

// CreateBackupWithOptions is similar to CreateBackup but lets the user specify additional options.
func (ac *AdminClient) CreateBackupWithOptions(ctx context.Context, table, cluster, backup string, expireTime time.Time, opts ...BackupOption) error {
return ac.createBackup(ctx, table, cluster, backup, expireTime, opts...)
}

func (ac *AdminClient) createBackup(ctx context.Context, table, cluster, backup string, expireTime time.Time, opts ...BackupOption) error {
bhshkh marked this conversation as resolved.
Show resolved Hide resolved
ctx = mergeOutgoingMetadata(ctx, ac.md)
prefix := ac.instancePrefix()

Expand All @@ -2166,7 +2217,18 @@ func (ac *AdminClient) CreateBackup(ctx context.Context, table, cluster, backup
SourceTable: prefix + "/tables/" + table,
},
}

o := backupOptions{}
for _, opt := range opts {
if opt != nil {
opt.apply(&o)
}
}
if o.backupType != nil {
req.Backup.BackupType = btapb.Backup_BackupType(*o.backupType)
}
if o.hotToStandardTime != nil {
req.Backup.HotToStandardTime = timestamppb.New(*o.hotToStandardTime)
}
op, err := ac.tClient.CreateBackup(ctx, req)
if err != nil {
return err
Expand Down Expand Up @@ -2263,17 +2325,29 @@ func newBackupInfo(backup *btapb.Backup) (*BackupInfo, error) {
return nil, fmt.Errorf("invalid expireTime: %v", err)
}
expireTime := backup.GetExpireTime().AsTime()

var htsTimePtr *time.Time
if backup.GetHotToStandardTime() != nil {
if err := backup.GetHotToStandardTime().CheckValid(); err != nil {
return nil, fmt.Errorf("invalid HotToStandardTime: %v", err)
}
htsTime := backup.GetHotToStandardTime().AsTime()
htsTimePtr = &htsTime
}

encryptionInfo := newEncryptionInfo(backup.EncryptionInfo)
bi := BackupInfo{
Name: name,
SourceTable: tableID,
SourceBackup: backup.SourceBackup,
SizeBytes: backup.SizeBytes,
StartTime: startTime,
EndTime: endTime,
ExpireTime: expireTime,
State: backup.State.String(),
EncryptionInfo: encryptionInfo,
Name: name,
SourceTable: tableID,
SourceBackup: backup.SourceBackup,
SizeBytes: backup.SizeBytes,
StartTime: startTime,
EndTime: endTime,
ExpireTime: expireTime,
State: backup.State.String(),
EncryptionInfo: encryptionInfo,
BackupType: BackupType(backup.GetBackupType()),
HotToStandardTime: htsTimePtr,
}

return &bi, nil
Expand Down Expand Up @@ -2303,6 +2377,25 @@ func (it *BackupIterator) Next() (*BackupInfo, error) {
return item, nil
}

// BackupType denotes the type of the backup.
type BackupType int32

const (
// BackupTypeUnspecified denotes that backup type has not been specified.
BackupTypeUnspecified BackupType = 0

// BackupTypeStandard is the default type for Cloud Bigtable managed backups. Supported for
// backups created in both HDD and SSD instances. Requires optimization when
// restored to a table in an SSD instance.
BackupTypeStandard BackupType = 1

// BackupTypeHot is a backup type with faster restore to SSD performance. Only supported for
// backups created in SSD instances. A new SSD table restored from a hot
// backup reaches production performance more quickly than a standard
// backup.
BackupTypeHot BackupType = 2
)

// BackupInfo contains backup metadata. This struct is read-only.
type BackupInfo struct {
Name string
Expand All @@ -2314,6 +2407,16 @@ type BackupInfo struct {
ExpireTime time.Time
State string
EncryptionInfo *EncryptionInfo
BackupType BackupType

// The time at which the hot backup will be converted to a standard backup.
// Once the `hot_to_standard_time` has passed, Cloud Bigtable will convert the
// hot backup to a standard backup. This value must be greater than the backup
// creation time by:
// - At least 24 hours
//
// This field only applies for hot backups.
HotToStandardTime *time.Time
}

// BackupInfo gets backup metadata.
Expand Down Expand Up @@ -2371,6 +2474,29 @@ func (ac *AdminClient) UpdateBackup(ctx context.Context, cluster, backup string,
return err
}

// UpdateBackupHotToStandardTime updates the HotToStandardTime of a hot backup.
func (ac *AdminClient) UpdateBackupHotToStandardTime(ctx context.Context, cluster, backup string, hotToStandardTime *time.Time) error {
bhshkh marked this conversation as resolved.
Show resolved Hide resolved
ctx = mergeOutgoingMetadata(ctx, ac.md)
backupPath := ac.backupPath(cluster, ac.instance, backup)

updateMask := &field_mask.FieldMask{}
updateMask.Paths = append(updateMask.Paths, "hot_to_standard_time")

req := &btapb.UpdateBackupRequest{
Backup: &btapb.Backup{
Name: backupPath,
},
UpdateMask: updateMask,
}

if hotToStandardTime != nil {
req.Backup.HotToStandardTime = timestamppb.New(*hotToStandardTime)
}

_, err := ac.tClient.UpdateBackup(ctx, req)
return err
}

// AuthorizedViewConf contains information about an authorized view.
type AuthorizedViewConf struct {
TableID string
Expand Down
Loading
Loading