Skip to content

Commit 1e6ae60

Browse files
authored
Allow validation room to report as occupied in scheduler update (configurable) (#697)
1 parent 536e50a commit 1e6ae60

File tree

4 files changed

+76
-7
lines changed

4 files changed

+76
-7
lines changed

config/config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ services:
9494
roomDeletionTimeoutMillis: 120000
9595
roomAllocationTTLMillis: 300000
9696
roomValidationAttempts: 3
97+
roomValidationAcceptOccupiedStatus: false
9798
operationManager:
9899
operationLeaseTTLMillis: 5000
99100
eventsForwarder:

internal/core/operations/schedulers/newversion/executor.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ import (
4444

4545
// Config defines configurations for the Executor.
4646
type Config struct {
47-
RoomInitializationTimeout time.Duration
48-
RoomValidationTimeout time.Duration
49-
RoomValidationAttempts int
47+
RoomInitializationTimeout time.Duration
48+
RoomValidationTimeout time.Duration
49+
RoomValidationAttempts int
50+
RoomValidationAcceptOccupiedStatus bool
5051
}
5152

5253
// Executor holds the dependecies to execute the operation to create a new scheduler version.
@@ -196,10 +197,18 @@ func (ex *Executor) validateGameRoomCreation(ctx context.Context, scheduler *ent
196197
timeoutContext, cancelFunc := context.WithTimeout(ctx, duration)
197198
defer cancelFunc()
198199

200+
status := []game_room.GameRoomStatus{game_room.GameStatusReady, game_room.GameStatusError}
201+
if ex.config.RoomValidationAcceptOccupiedStatus {
202+
// WARNING: Accepting occupied as valid is dangerous. Use only when validation
203+
// rooms may be picked up by the matchmaker before reporting ready status. If a new room
204+
// version incorrectly transitions to occupied, Maestro may scale up indefinitely.
205+
status = append(status, game_room.GameStatusOccupied)
206+
}
207+
199208
roomStatus, waitRoomErr := ex.roomManager.WaitRoomStatus(
200209
timeoutContext,
201210
gameRoom,
202-
[]game_room.GameRoomStatus{game_room.GameStatusReady, game_room.GameStatusError},
211+
status,
203212
)
204213

205214
if waitRoomErr != nil {
@@ -214,6 +223,9 @@ func (ex *Executor) validateGameRoomCreation(ctx context.Context, scheduler *ent
214223
case game_room.GameStatusReady:
215224
logger.Sugar().Infof("validation room with ID: %s is ready", gameRoom.ID)
216225
return nil
226+
case game_room.GameStatusOccupied:
227+
logger.Sugar().Infof("validation room with ID: %s is occupied", gameRoom.ID)
228+
return nil
217229
case game_room.GameStatusError:
218230
logger.Sugar().Infof("validation room with ID: %s is in error state", gameRoom.ID)
219231
var statusDescription string

internal/core/operations/schedulers/newversion/executor_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,59 @@ func TestExecutor_Execute(t *testing.T) {
160160
require.Nil(t, result)
161161
})
162162

163+
t.Run("should succeed - major version update, game room status is occupied with config enabled, returns no error -> enqueue switch active version op", func(t *testing.T) {
164+
mockCtrl := gomock.NewController(t)
165+
166+
currentActiveScheduler := newValidSchedulerWithImageVersion("image-v1")
167+
newScheduler := *newValidSchedulerWithImageVersion("image-v2")
168+
newSchedulerExpectedVersion := "v2.0.0"
169+
op := &operation.Operation{
170+
ID: "123",
171+
Status: operation.StatusInProgress,
172+
DefinitionName: newversion.OperationName,
173+
SchedulerName: newScheduler.Name,
174+
}
175+
operationDef := &newversion.Definition{NewScheduler: &newScheduler}
176+
roomManager := mockports.NewMockRoomManager(mockCtrl)
177+
schedulerManager := mockports.NewMockSchedulerManager(mockCtrl)
178+
operationsManager := mockports.NewMockOperationManager(mockCtrl)
179+
switchOpID := "switch-active-version-op-id"
180+
config := newversion.Config{
181+
RoomInitializationTimeout: time.Duration(120000),
182+
RoomValidationTimeout: time.Duration(120000),
183+
RoomValidationAttempts: 1,
184+
RoomValidationAcceptOccupiedStatus: true,
185+
}
186+
187+
executor := newversion.NewExecutor(roomManager, schedulerManager, operationsManager, config)
188+
189+
schedulerVersions := []*entities.SchedulerVersion{{Version: "v1.0.0"}, {Version: "v1.1.0"}, {Version: "v1.2.0"}}
190+
gameRoom := &game_room.GameRoom{ID: "id-1"}
191+
192+
roomManager.EXPECT().CreateRoom(gomock.Any(), gomock.Any(), true).Return(gameRoom, nil, nil)
193+
roomManager.EXPECT().WaitRoomStatus(gomock.Any(), gameRoom, []game_room.GameRoomStatus{game_room.GameStatusReady, game_room.GameStatusError, game_room.GameStatusOccupied}).Return(game_room.GameStatusOccupied, nil)
194+
roomManager.EXPECT().DeleteRoom(gomock.Any(), gomock.Any(), remove.NewVersionValidationFinished).Return(nil)
195+
196+
schedulerManager.
197+
EXPECT().
198+
CreateNewSchedulerVersionAndEnqueueSwitchVersion(gomock.Any(), gomock.Any()).
199+
DoAndReturn(
200+
func(ctx context.Context, scheduler *entities.Scheduler) (string, error) {
201+
require.Equal(t, newSchedulerExpectedVersion, scheduler.Spec.Version)
202+
return switchOpID, nil
203+
})
204+
schedulerManager.EXPECT().GetActiveScheduler(gomock.Any(), newScheduler.Name).Return(currentActiveScheduler, nil)
205+
schedulerManager.EXPECT().GetSchedulerVersions(gomock.Any(), newScheduler.Name).Return(schedulerVersions, nil)
206+
schedulerManager.EXPECT().UpdateScheduler(gomock.Any(), currentActiveScheduler).Return(nil)
207+
operationsManager.EXPECT().AppendOperationEventToExecutionHistory(gomock.Any(), op, "Major version detected, starting game room validation process...")
208+
operationsManager.EXPECT().AppendOperationEventToExecutionHistory(gomock.Any(), op, "1º Attempt: Game room validation success!")
209+
operationsManager.EXPECT().AppendOperationEventToExecutionHistory(gomock.Any(), op, fmt.Sprintf("enqueued switch active version operation with id: %s", switchOpID))
210+
211+
result := executor.Execute(context.Background(), op, operationDef)
212+
213+
require.Nil(t, result)
214+
})
215+
163216
t.Run("should succeed - major version update, game room is valid, greatest major version is v3, returns no error -> enqueue switch active version op", func(t *testing.T) {
164217
mockCtrl := gomock.NewController(t)
165218

internal/service/config.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const (
4545
roomInitializationTimeoutMillisConfigPath = "services.roomManager.roomInitializationTimeoutMillis"
4646
roomValidationTimeoutMillisConfigPath = "services.roomManager.roomValidationTimeoutMillis"
4747
roomRoomValidationAttemptsConfigPath = "services.roomManager.roomValidationAttempts"
48+
roomValidationAcceptOccupiedStatusConfigPath = "services.roomManager.roomValidationAcceptOccupiedStatus"
4849
roomPingTimeoutMillisConfigPath = "services.roomManager.roomPingTimeoutMillis"
4950
roomDeletionTimeoutMillisConfigPath = "services.roomManager.roomDeletionTimeoutMillis"
5051
roomAllocationTTLMillisConfigPath = "services.roomManager.roomAllocationTTLMillis"
@@ -64,11 +65,13 @@ func NewCreateSchedulerVersionConfig(c config.Config) newversion.Config {
6465
if roomValidationAttempts < 1 {
6566
roomValidationAttempts = 1
6667
}
68+
acceptOccupiedStatus := c.GetBool(roomValidationAcceptOccupiedStatusConfigPath)
6769

6870
createSchedulerVersionConfig := newversion.Config{
69-
RoomInitializationTimeout: initializationTimeout,
70-
RoomValidationTimeout: validationTimeout,
71-
RoomValidationAttempts: roomValidationAttempts,
71+
RoomInitializationTimeout: initializationTimeout,
72+
RoomValidationTimeout: validationTimeout,
73+
RoomValidationAttempts: roomValidationAttempts,
74+
RoomValidationAcceptOccupiedStatus: acceptOccupiedStatus,
7275
}
7376

7477
return createSchedulerVersionConfig

0 commit comments

Comments
 (0)