From 93964f2504dc4f763c4b3595cdf4fce08a7503f1 Mon Sep 17 00:00:00 2001 From: Iuliia Sidorina Date: Tue, 10 Dec 2024 14:59:26 +0100 Subject: [PATCH] feat(backup_schedule_service): add limit for number of schedules per database (#113) --- cmd/integration/make_backup/main.go | 17 +++++++ cmd/ydbcp/main.go | 2 +- internal/config/config.go | 1 + .../backup_schedule_service.go | 47 +++++++++++++++++++ local_config.yaml | 4 +- 5 files changed, 69 insertions(+), 2 deletions(-) diff --git a/cmd/integration/make_backup/main.go b/cmd/integration/make_backup/main.go index 4e6a3eae..7db401bf 100644 --- a/cmd/integration/make_backup/main.go +++ b/cmd/integration/make_backup/main.go @@ -312,6 +312,23 @@ func main() { if err != nil { log.Panicf("failed to create backup schedule: %v", err) } + + // local config has schedules_limit_per_db = 1, so we should not be able to create another schedule for this db + _, err = scheduleClient.CreateBackupSchedule( + context.Background(), &pb.CreateBackupScheduleRequest{ + ContainerId: containerID, + DatabaseName: databaseName, + Endpoint: databaseEndpoint, + ScheduleName: "anotherSchedule", + ScheduleSettings: &pb.BackupScheduleSettings{ + SchedulePattern: &pb.BackupSchedulePattern{Crontab: "* * * * *"}, + }, + }, + ) + if err == nil { + log.Panicf("we've created more schedules than schedules_limit_per_db") + } + schedules, err = scheduleClient.ListBackupSchedules( context.Background(), &pb.ListBackupSchedulesRequest{ ContainerId: containerID, diff --git a/cmd/ydbcp/main.go b/cmd/ydbcp/main.go index afa86f1e..7dad3ccd 100644 --- a/cmd/ydbcp/main.go +++ b/cmd/ydbcp/main.go @@ -129,7 +129,7 @@ func main() { configInstance.ClientConnection.AllowInsecureEndpoint, ).Register(server) operation.NewOperationService(dbConnector, authProvider).Register(server) - backup_schedule.NewBackupScheduleService(dbConnector, clientConnector, authProvider).Register(server) + backup_schedule.NewBackupScheduleService(dbConnector, clientConnector, authProvider, configInstance).Register(server) if err := server.Start(ctx, &wg); err != nil { xlog.Error(ctx, "Error start GRPC server", zap.Error(err)) os.Exit(1) diff --git a/internal/config/config.go b/internal/config/config.go index 375eac8c..4d2b61cf 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -71,6 +71,7 @@ type Config struct { Auth AuthConfig `yaml:"auth"` GRPCServer GRPCServerConfig `yaml:"grpc_server"` MetricsServer MetricsServerConfig `yaml:"metrics_server"` + SchedulesLimitPerDB int `yaml:"schedules_limit_per_db" default:"10"` } var ( diff --git a/internal/server/services/backup_schedule/backup_schedule_service.go b/internal/server/services/backup_schedule/backup_schedule_service.go index 8cb34222..8d1f6bc7 100644 --- a/internal/server/services/backup_schedule/backup_schedule_service.go +++ b/internal/server/services/backup_schedule/backup_schedule_service.go @@ -8,6 +8,7 @@ import ( "time" "ydbcp/internal/auth" "ydbcp/internal/backup_operations" + "ydbcp/internal/config" "ydbcp/internal/connectors/client" "ydbcp/internal/connectors/db" "ydbcp/internal/connectors/db/yql/queries" @@ -32,6 +33,7 @@ type BackupScheduleService struct { clientConn client.ClientConnector auth ap.AuthProvider clock clockwork.Clock + config config.Config } func (s *BackupScheduleService) IncApiCallsCounter(methodName string, code codes.Code) { @@ -76,6 +78,49 @@ func (s *BackupScheduleService) CreateBackupSchedule( s.IncApiCallsCounter(methodName, status.Code(err)) return nil, err } + + schedules, err := s.driver.SelectBackupSchedules( + ctx, queries.NewReadTableQuery( + queries.WithTableName("BackupSchedules"), + queries.WithQueryFilters( + queries.QueryFilter{ + Field: "container_id", + Values: []table_types.Value{ + table_types.StringValueFromString(request.ContainerId), + }, + }, + queries.QueryFilter{ + Field: "database", + Values: []table_types.Value{ + table_types.StringValueFromString(request.DatabaseName), + }, + }, + ), + ), + ) + + if err != nil { + xlog.Error(ctx, "error getting backup schedules", zap.Error(err)) + s.IncApiCallsCounter(methodName, codes.Internal) + return nil, status.Error(codes.Internal, "error getting backup schedules") + } + + if len(schedules)+1 > s.config.SchedulesLimitPerDB { + xlog.Error(ctx, "can't create backup schedule, limit exceeded for database", + zap.String("database", request.DatabaseName), + zap.String("container", request.ContainerId), + zap.Int("limit", s.config.SchedulesLimitPerDB), + ) + s.IncApiCallsCounter(methodName, codes.FailedPrecondition) + return nil, status.Errorf( + codes.FailedPrecondition, + "can't create backup schedule, limit exceeded for database: %s, container: %s, limit: %d", + request.DatabaseName, + request.ContainerId, + s.config.SchedulesLimitPerDB, + ) + } + if request.ScheduleSettings == nil { xlog.Error( ctx, "no backup schedule settings for CreateBackupSchedule", zap.String("request", request.String()), @@ -525,11 +570,13 @@ func NewBackupScheduleService( driver db.DBConnector, clientConn client.ClientConnector, auth ap.AuthProvider, + config config.Config, ) *BackupScheduleService { return &BackupScheduleService{ driver: driver, clientConn: clientConn, auth: auth, clock: clockwork.NewRealClock(), + config: config, } } diff --git a/local_config.yaml b/local_config.yaml index 418b6b56..dceca100 100644 --- a/local_config.yaml +++ b/local_config.yaml @@ -27,4 +27,6 @@ grpc_server: metrics_server: bind_address: 127.0.0.1 - bind_port: 9090 \ No newline at end of file + bind_port: 9090 + +schedules_limit_per_db: 1 \ No newline at end of file