Skip to content

Commit

Permalink
fix(room_manager): only sort rooms removal if major version diff (#640)
Browse files Browse the repository at this point in the history
We only want to apply sorting by version if the room has a major version of
difference compared to the active scheduler version. This prevents minor
updates forcing the sort, which would cause occupied rooms to be deleted
first (from a prior minor version) when we don't want that behavior. Thus,
check if it has a major version diff between room and active scheduler
  • Loading branch information
hspedro authored Oct 9, 2024
1 parent 4fe0422 commit dd58d08
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 10 deletions.
15 changes: 15 additions & 0 deletions internal/core/entities/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"errors"
"time"

"github.com/Masterminds/semver"
"github.com/topfreegames/maestro/internal/core/entities/autoscaling"
"github.com/topfreegames/maestro/internal/core/entities/port"

Expand Down Expand Up @@ -202,3 +203,17 @@ func (s *Scheduler) HasValidPortRangeConfiguration() error {

return nil
}

func (s *Scheduler) IsSameMajorVersion(otherVersion string) bool {
otherVer, err := semver.NewVersion(otherVersion)
if err != nil {
return false
}

schedulerVer, err := semver.NewVersion(s.Spec.Version)
if err != nil {
return false
}

return otherVer.Major() == schedulerVer.Major()
}
28 changes: 28 additions & 0 deletions internal/core/entities/scheduler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,3 +399,31 @@ func TestHasValidPortRangeConfiguration(t *testing.T) {
})
}
}

func TestIsSameMajorVersion(t *testing.T) {
s := &entities.Scheduler{}
s.Spec.Version = "v10.5.0"

testCases := []struct {
name string
otherVersion string
expected bool
}{
{"Both versions are the same", "v10.5.0", true},
{"Only minor version difference", "v10.6.0", true},
{"Only bugfix version difference", "v10.5.1", true},
{"otherVersion without v prefix", "10.0.0", true},
{"Major version difference", "v11.0.0", false},
{"Empty otherVersion", "", false},
{"otherVersion not semantic", "version10", false},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result := s.IsSameMajorVersion(tc.otherVersion)
if result != tc.expected {
t.Errorf("Test %s failed: expected %v, got %v", tc.name, tc.expected, result)
}
})
}
}
2 changes: 1 addition & 1 deletion internal/core/operations/rooms/remove/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (e *Executor) removeRoomsByAmount(ctx context.Context, logger *zap.Logger,
}

logger.Info("removing rooms by amount sorting by version",
zap.Array("originalRoomsOrder", zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error {
zap.Array("rooms:", zapcore.ArrayMarshalerFunc(func(enc zapcore.ArrayEncoder) error {
for _, room := range rooms {
enc.AppendString(fmt.Sprintf("%s-%s-%s", room.ID, room.Version, room.Status.String()))
}
Expand Down
4 changes: 2 additions & 2 deletions internal/core/services/rooms/room_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func (m *RoomManager) ListRoomsWithDeletionPriority(ctx context.Context, activeS
return nil, fmt.Errorf("failed to fetch room information: %w", err)
}

room = &game_room.GameRoom{ID: roomID, SchedulerID: activeScheduler.Name, Status: game_room.GameStatusError}
room = &game_room.GameRoom{ID: roomID, SchedulerID: activeScheduler.Name, Status: game_room.GameStatusError, Version: activeScheduler.Spec.Version}
}

// Select Terminating rooms to be re-deleted. This is useful for fixing any desync state.
Expand All @@ -257,7 +257,7 @@ func (m *RoomManager) ListRoomsWithDeletionPriority(ctx context.Context, activeS
}

isRoomActive := room.Status == game_room.GameStatusOccupied || room.Status == game_room.GameStatusReady || room.Status == game_room.GameStatusPending
if isRoomActive && room.Version == activeScheduler.Spec.Version {
if isRoomActive && activeScheduler.IsSameMajorVersion(room.Version) {
activeVersionRoomPool = append(activeVersionRoomPool, room)
} else {
toDeleteRooms = append(toDeleteRooms, room)
Expand Down
19 changes: 12 additions & 7 deletions internal/core/services/rooms/room_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,24 +580,29 @@ func TestRoomManager_ListRoomsWithDeletionPriority(t *testing.T) {
ctx := context.Background()
scheduler := newValidScheduler()
availableRooms := []*game_room.GameRoom{
{ID: "first-room", SchedulerID: scheduler.Name, Status: game_room.GameStatusReady},
{ID: "first-room", SchedulerID: scheduler.Name, Status: game_room.GameStatusReady, Version: scheduler.Spec.Version},
}

notFoundRoomID := "second-room"

roomStorage.EXPECT().GetRoomIDsByStatus(ctx, scheduler.Name, gomock.Any()).Return([]string{availableRooms[0].ID}, nil).AnyTimes()
roomStorage.EXPECT().GetRoomIDsByStatus(ctx, scheduler.Name, game_room.GameStatusError).Return([]string{}, nil).Times(1)
roomStorage.EXPECT().GetRoomIDsByStatus(ctx, scheduler.Name, game_room.GameStatusPending).Return([]string{}, nil).Times(1)
roomStorage.EXPECT().GetRoomIDsByStatus(ctx, scheduler.Name, game_room.GameStatusReady).Return([]string{availableRooms[0].ID}, nil).Times(1)
roomStorage.EXPECT().GetRoomIDsByStatus(ctx, scheduler.Name, game_room.GameStatusOccupied).Return([]string{}, nil).Times(1)
roomStorage.EXPECT().GetRoomIDsByLastPing(ctx, scheduler.Name, gomock.Any()).Return([]string{notFoundRoomID}, nil)

roomStorage.EXPECT().GetRoom(ctx, scheduler.Name, availableRooms[0].ID).Return(availableRooms[0], nil)

getRoomErr := porterrors.NewErrNotFound("failed to get")
roomStorage.EXPECT().GetRoom(ctx, scheduler.Name, notFoundRoomID).Return(nil, getRoomErr)
roomStorage.EXPECT().GetRoom(ctx, scheduler.Name, availableRooms[0].ID).Return(availableRooms[0], nil)

rooms, err := roomManager.ListRoomsWithDeletionPriority(ctx, scheduler, 2)
require.NoError(t, err)

availableRooms = append(availableRooms, &game_room.GameRoom{ID: notFoundRoomID, SchedulerID: scheduler.Name, Status: game_room.GameStatusError})
require.Equal(t, rooms, availableRooms)
expectedRooms := []*game_room.GameRoom{
{ID: notFoundRoomID, SchedulerID: scheduler.Name, Status: game_room.GameStatusError, Version: scheduler.Spec.Version},
{ID: "first-room", SchedulerID: scheduler.Name, Status: game_room.GameStatusReady, Version: scheduler.Spec.Version},
}

require.Equal(t, rooms, expectedRooms)
})

t.Run("when error happens while fetch a room it returns error", func(t *testing.T) {
Expand Down

0 comments on commit dd58d08

Please sign in to comment.