Skip to content

Commit

Permalink
fix(scheduler): optional downThreshold (#619)
Browse files Browse the repository at this point in the history
Many clients forget to update downThreshold or simply do not
care how maestro uses it for downscale. To reduce their
cognitive load, we can make the parameter optional and if not
set always downscale (0.99)
  • Loading branch information
hspedro authored May 10, 2024
1 parent 471ce4f commit c016f5d
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 109 deletions.
4 changes: 2 additions & 2 deletions docs/tutorials/Autoscaling.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ So basically Maestro will constantly try to maintain a certain percentage of roo
actual room occupancy rate (number of rooms in **occupied** state).

#### Room Occupancy Policy Parameters
- **readyTarget** [float]: The percentage (in decimal value) of rooms that Maestro should try to keep in **ready** state, must be a value between 0.1 and 0.9.
- **downThreshold** [float]: It adjusts how often maestro scale down Game Rooms, where 0.99 means that maestro will always scale down a Game Room when it is free (respecting the readyTarget), and 0 means that maestro will never scale down. Must be a value between 0 and 0.99.
- **readyTarget** [float]: The percentage (in decimal value) of rooms that Maestro should try to keep in **ready** state, must be a value greater than 0 and less than 1.
- **downThreshold** [float]: It adjusts how often maestro scale down Game Rooms, where 0.99 means that maestro will always scale down a Game Room when it is free (respecting the readyTarget), and a value close to 0 means that maestro will almost never scale down. Must be a value greater than 0 and less than 1.

#### Example

Expand Down
6 changes: 5 additions & 1 deletion internal/api/handlers/requestadapters/schedulers.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,9 @@ func fromApiRoomOccupancyPolicyToEntity(roomOccupancy *api.RoomOccupancy) *autos
roomOccupancyParam.ReadyTarget = float64(readyTarget)

downThreshold := roomOccupancy.GetDownThreshold()
if downThreshold == 0 {
downThreshold = autoscaling.DefaultDownThreshold
}
roomOccupancyParam.DownThreshold = float64(downThreshold)

return roomOccupancyParam
Expand Down Expand Up @@ -538,9 +541,10 @@ func getRoomOccupancy(roomOccupancyParameters *autoscaling.RoomOccupancyParams)
if roomOccupancyParameters == nil {
return nil
}
downThresholdVal := float32(roomOccupancyParameters.DownThreshold)
return &api.RoomOccupancy{
ReadyTarget: float32(roomOccupancyParameters.ReadyTarget),
DownThreshold: float32(roomOccupancyParameters.DownThreshold),
DownThreshold: &downThresholdVal,
}
}

Expand Down
17 changes: 11 additions & 6 deletions internal/api/handlers/requestadapters/schedulers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ func TestFromApiPatchSchedulerRequestToChangeMap(t *testing.T) {
Parameters: &api.PolicyParameters{
RoomOccupancy: &api.RoomOccupancy{
ReadyTarget: genericFloat32,
DownThreshold: genericFloat32,
DownThreshold: &genericFloat32,
},
},
},
Expand Down Expand Up @@ -380,7 +380,7 @@ func TestFromApiPatchSchedulerRequestToChangeMap(t *testing.T) {
Parameters: &api.PolicyParameters{
RoomOccupancy: &api.RoomOccupancy{
ReadyTarget: genericFloat32,
DownThreshold: genericFloat32,
DownThreshold: &genericFloat32,
},
},
},
Expand Down Expand Up @@ -645,7 +645,8 @@ func TestFromApiCreateSchedulerRequestToEntity(t *testing.T) {
Type: autoscaling.RoomOccupancy,
Parameters: autoscaling.PolicyParameters{
RoomOccupancy: &autoscaling.RoomOccupancyParams{
ReadyTarget: float64(genericFloat32),
ReadyTarget: float64(genericFloat32),
DownThreshold: float64(autoscaling.DefaultDownThreshold),
},
},
},
Expand Down Expand Up @@ -1299,7 +1300,8 @@ func TestFromApiNewSchedulerVersionRequestToEntity(t *testing.T) {
Type: autoscaling.RoomOccupancy,
Parameters: autoscaling.PolicyParameters{
RoomOccupancy: &autoscaling.RoomOccupancyParams{
ReadyTarget: float64(genericFloat32),
ReadyTarget: float64(genericFloat32),
DownThreshold: float64(autoscaling.DefaultDownThreshold),
},
},
},
Expand Down Expand Up @@ -1363,6 +1365,7 @@ func TestFromEntitySchedulerToResponse(t *testing.T) {
genericValidVersion := "v1.0.0"
genericStringList := []string{"some-value", "another-value"}
genericTime := time.Now()
genericFloat32 := float32(0.3)

testCases := []struct {
Title string
Expand Down Expand Up @@ -1441,7 +1444,8 @@ func TestFromEntitySchedulerToResponse(t *testing.T) {
Type: autoscaling.RoomOccupancy,
Parameters: autoscaling.PolicyParameters{
RoomOccupancy: &autoscaling.RoomOccupancyParams{
ReadyTarget: 0.3,
ReadyTarget: float64(genericFloat32),
DownThreshold: float64(genericFloat32),
},
},
},
Expand Down Expand Up @@ -1532,7 +1536,8 @@ func TestFromEntitySchedulerToResponse(t *testing.T) {
Type: "roomOccupancy",
Parameters: &api.PolicyParameters{
RoomOccupancy: &api.RoomOccupancy{
ReadyTarget: float32(0.3),
ReadyTarget: genericFloat32,
DownThreshold: &genericFloat32,
},
},
},
Expand Down
7 changes: 4 additions & 3 deletions internal/core/entities/autoscaling/autoscaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ type PolicyType string
const (
// RoomOccupancy is an implemented policy in maestro autoscaler,
// it uses the number of occupied rooms and a ready rooms target percentage to calculate the desired number of rooms in a scheduler.
RoomOccupancy PolicyType = "roomOccupancy"
RoomOccupancy PolicyType = "roomOccupancy"
DefaultDownThreshold float32 = 0.99
)

// Autoscaling represents the autoscaling configuration for a scheduler.
Expand Down Expand Up @@ -85,7 +86,7 @@ type PolicyParameters struct {
// RoomOccupancyParams represents the parameters accepted by rooms occupancy autoscaling properties.
type RoomOccupancyParams struct {
// ReadyTarget indicates the target percentage of ready rooms a scheduler should maintain.
ReadyTarget float64 `validate:"gt=0,lte=0.9"`
ReadyTarget float64 `validate:"gt=0,lt=1"`
// DownThreshold indicates the percentage of occupied rooms a scheduler should have to trigger a downscale event.
DownThreshold float64 `validate:"gte=0,lte=0.99"`
DownThreshold float64 `validate:"gt=0,lt=1"`
}
14 changes: 11 additions & 3 deletions internal/core/entities/autoscaling/autoscaling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,21 @@ func TestNewAutoscaling(t *testing.T) {
validationErrs = err.(validator.ValidationErrors)
assert.Equal(t, "RoomOccupancy must not be nil for RoomOccupancy policy type", validationErrs[0].Translate(translator))

_, err = NewAutoscaling(true, 1, 10, 10, Policy{Type: "roomOccupancy", Parameters: PolicyParameters{RoomOccupancy: &RoomOccupancyParams{ReadyTarget: 0.0}}})
_, err = NewAutoscaling(true, 1, 10, 10, Policy{Type: "roomOccupancy", Parameters: PolicyParameters{RoomOccupancy: &RoomOccupancyParams{ReadyTarget: 0.0, DownThreshold: 0.1}}})
validationErrs = err.(validator.ValidationErrors)
assert.Equal(t, "ReadyTarget must be greater than 0", validationErrs[0].Translate(translator))

_, err = NewAutoscaling(true, 1, 10, 10, Policy{Type: "roomOccupancy", Parameters: PolicyParameters{RoomOccupancy: &RoomOccupancyParams{ReadyTarget: 0.91}}})
_, err = NewAutoscaling(true, 1, 10, 10, Policy{Type: "roomOccupancy", Parameters: PolicyParameters{RoomOccupancy: &RoomOccupancyParams{ReadyTarget: 1, DownThreshold: 0.1}}})
validationErrs = err.(validator.ValidationErrors)
assert.Equal(t, "ReadyTarget must be 0.9 or less", validationErrs[0].Translate(translator))
assert.Equal(t, "ReadyTarget must be less than 1", validationErrs[0].Translate(translator))

_, err = NewAutoscaling(true, 1, 10, 10, Policy{Type: "roomOccupancy", Parameters: PolicyParameters{RoomOccupancy: &RoomOccupancyParams{ReadyTarget: 0.5, DownThreshold: 0}}})
validationErrs = err.(validator.ValidationErrors)
assert.Equal(t, "DownThreshold must be greater than 0", validationErrs[0].Translate(translator))

_, err = NewAutoscaling(true, 1, 10, 10, Policy{Type: "roomOccupancy", Parameters: PolicyParameters{RoomOccupancy: &RoomOccupancyParams{ReadyTarget: 0.5, DownThreshold: 1}}})
validationErrs = err.(validator.ValidationErrors)
assert.Equal(t, "DownThreshold must be less than 1", validationErrs[0].Translate(translator))
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func (p *Policy) CalculateDesiredNumberOfRooms(policyParameters autoscaling.Poli

readyTarget := policyParameters.RoomOccupancy.ReadyTarget
if readyTarget >= float64(1) || readyTarget <= 0 {
return -1, errors.New("Ready target must be between 0 and 1")
return -1, errors.New("ready target must be greater than 0 and less than 1")
}

if _, ok := currentState[OccupiedRoomsKey].(int); !ok {
Expand All @@ -105,12 +105,12 @@ func (p *Policy) CanDownscale(policyParameters autoscaling.PolicyParameters, cur

downThreshold := policyParameters.RoomOccupancy.DownThreshold
if downThreshold >= float64(1) || downThreshold <= 0 {
return false, errors.New("Downscale threshold must be between 0 and 1")
return false, errors.New("downscale threshold must be greater than 0 and less than 1")
}

readyTarget := policyParameters.RoomOccupancy.ReadyTarget
if readyTarget >= float64(1) || readyTarget <= 0 {
return false, errors.New("Ready target must be between 0 and 1")
return false, errors.New("ready target must be greater than 0 and less than 1")
}

readyRooms, ok := currentState[ReadyRoomsKey].(int)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func TestCalculateDesiredNumberOfRooms(t *testing.T) {
}

_, err := policy.CalculateDesiredNumberOfRooms(policyParams, schedulerState)
assert.EqualError(t, err, "Ready target must be between 0 and 1")
assert.EqualError(t, err, "ready target must be greater than 0 and less than 1")
})
t.Run("when ready target is greater than 1", func(t *testing.T) {
readyTarget := float64(1.1)
Expand All @@ -212,7 +212,7 @@ func TestCalculateDesiredNumberOfRooms(t *testing.T) {
}

_, err := policy.CalculateDesiredNumberOfRooms(policyParams, schedulerState)
assert.EqualError(t, err, "Ready target must be between 0 and 1")
assert.EqualError(t, err, "ready target must be greater than 0 and less than 1")
})
t.Run("when ready target is 0", func(t *testing.T) {
readyTarget := float64(0.0)
Expand All @@ -229,7 +229,7 @@ func TestCalculateDesiredNumberOfRooms(t *testing.T) {
}

_, err := policy.CalculateDesiredNumberOfRooms(policyParams, schedulerState)
assert.EqualError(t, err, "Ready target must be between 0 and 1")
assert.EqualError(t, err, "ready target must be greater than 0 and less than 1")
})
t.Run("when ready target is lower than 0", func(t *testing.T) {
readyTarget := float64(-0.1)
Expand All @@ -246,7 +246,7 @@ func TestCalculateDesiredNumberOfRooms(t *testing.T) {
}

_, err := policy.CalculateDesiredNumberOfRooms(policyParams, schedulerState)
assert.EqualError(t, err, "Ready target must be between 0 and 1")
assert.EqualError(t, err, "ready target must be greater than 0 and less than 1")
})
})
}
Expand Down Expand Up @@ -380,7 +380,7 @@ func TestCanDownscale(t *testing.T) {
}

_, err := policy.CanDownscale(policyParams, schedulerState)
assert.EqualError(t, err, "Downscale threshold must be between 0 and 1")
assert.EqualError(t, err, "downscale threshold must be greater than 0 and less than 1")
})
t.Run("when ready target is greater than 1", func(t *testing.T) {
downThreshold := float64(1.1)
Expand All @@ -399,7 +399,7 @@ func TestCanDownscale(t *testing.T) {
}

_, err := policy.CanDownscale(policyParams, schedulerState)
assert.EqualError(t, err, "Downscale threshold must be between 0 and 1")
assert.EqualError(t, err, "downscale threshold must be greater than 0 and less than 1")
})
t.Run("when down threshold is 0", func(t *testing.T) {
downThreshold := float64(0.0)
Expand All @@ -418,7 +418,7 @@ func TestCanDownscale(t *testing.T) {
}

_, err := policy.CanDownscale(policyParams, schedulerState)
assert.EqualError(t, err, "Downscale threshold must be between 0 and 1")
assert.EqualError(t, err, "downscale threshold must be greater than 0 and less than 1")
})
t.Run("when down threshold is lower than 0", func(t *testing.T) {
downThreshold := float64(-0.1)
Expand All @@ -437,7 +437,7 @@ func TestCanDownscale(t *testing.T) {
}

_, err := policy.CanDownscale(policyParams, schedulerState)
assert.EqualError(t, err, "Downscale threshold must be between 0 and 1")
assert.EqualError(t, err, "downscale threshold must be greater than 0 and less than 1")
})
})
}
18 changes: 12 additions & 6 deletions internal/core/services/schedulers/patch/patch_scheduler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,8 @@ func TestPatchScheduler(t *testing.T) {
Type: autoscaling.RoomOccupancy,
Parameters: autoscaling.PolicyParameters{
RoomOccupancy: &autoscaling.RoomOccupancyParams{
ReadyTarget: float64(genericFloat32),
ReadyTarget: float64(genericFloat32),
DownThreshold: float64(genericFloat32),
},
},
},
Expand All @@ -720,7 +721,8 @@ func TestPatchScheduler(t *testing.T) {
Type: autoscaling.RoomOccupancy,
Parameters: autoscaling.PolicyParameters{
RoomOccupancy: &autoscaling.RoomOccupancyParams{
ReadyTarget: float64(genericFloat32),
ReadyTarget: float64(genericFloat32),
DownThreshold: float64(genericFloat32),
},
},
},
Expand Down Expand Up @@ -752,7 +754,8 @@ func TestPatchScheduler(t *testing.T) {
Type: autoscaling.RoomOccupancy,
Parameters: autoscaling.PolicyParameters{
RoomOccupancy: &autoscaling.RoomOccupancyParams{
ReadyTarget: float64(genericFloat32),
ReadyTarget: float64(genericFloat32),
DownThreshold: float64(genericFloat32),
},
},
},
Expand Down Expand Up @@ -784,7 +787,8 @@ func TestPatchScheduler(t *testing.T) {
Type: autoscaling.RoomOccupancy,
Parameters: autoscaling.PolicyParameters{
RoomOccupancy: &autoscaling.RoomOccupancyParams{
ReadyTarget: float64(genericFloat32),
ReadyTarget: float64(genericFloat32),
DownThreshold: float64(genericFloat32),
},
},
},
Expand Down Expand Up @@ -812,7 +816,8 @@ func TestPatchScheduler(t *testing.T) {
Type: autoscaling.RoomOccupancy,
Parameters: autoscaling.PolicyParameters{
RoomOccupancy: &autoscaling.RoomOccupancyParams{
ReadyTarget: float64(genericFloat32),
ReadyTarget: float64(genericFloat32),
DownThreshold: float64(genericFloat32),
},
},
},
Expand Down Expand Up @@ -990,7 +995,8 @@ func basicSchedulerToPatchSchedulerTests() *entities.Scheduler {
Type: autoscaling.RoomOccupancy,
Parameters: autoscaling.PolicyParameters{
RoomOccupancy: &autoscaling.RoomOccupancyParams{
ReadyTarget: float64(genericFloat32),
ReadyTarget: float64(genericFloat32),
DownThreshold: float64(genericFloat32),
},
},
},
Expand Down
Loading

0 comments on commit c016f5d

Please sign in to comment.