Skip to content

Commit 314d8e9

Browse files
joaobolognajoao.bologna
andauthored
Make add operation ctx timeout configurable (#682)
Co-authored-by: joao.bologna <[email protected]>
1 parent edd57a8 commit 314d8e9

File tree

4 files changed

+111
-18
lines changed

4 files changed

+111
-18
lines changed

config/config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ operations:
8282
add:
8383
limit: 1000
8484
batchSize: 40
85+
operationTimeout: 5s
8586
remove:
8687
limit: 50
8788

internal/core/operations/rooms/add/executor.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ import (
4141
)
4242

4343
const (
44-
DefaultAmountLimit = 1000
45-
DefaultBatchSize = 40
44+
DefaultAmountLimit = 1000
45+
DefaultBatchSize = 40
46+
DefaultOperationTimeout = 5 * time.Second
4647
)
4748

4849
type Config struct {
49-
AmountLimit int32
50-
BatchSize int32
50+
AmountLimit int32
51+
BatchSize int32
52+
OperationTimeout time.Duration
5153
}
5254

5355
type Executor struct {
@@ -68,6 +70,10 @@ func NewExecutor(roomManager ports.RoomManager, storage ports.SchedulerStorage,
6870
zap.L().Sugar().Infof("Add Executor - Batch size wrongly configured with %d, using default value %d", config.BatchSize, DefaultBatchSize)
6971
config.BatchSize = DefaultBatchSize
7072
}
73+
if config.OperationTimeout <= 0 {
74+
zap.L().Sugar().Infof("Add Executor - Operation timeout wrongly configured with %v, using default value %v", config.OperationTimeout, DefaultOperationTimeout)
75+
config.OperationTimeout = DefaultOperationTimeout
76+
}
7177
return &Executor{
7278
roomManager,
7379
storage,
@@ -119,7 +125,7 @@ func (ex *Executor) Execute(ctx context.Context, op *operation.Operation, defini
119125
go func() {
120126
defer wg.Done()
121127

122-
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
128+
ctx, cancel := context.WithTimeout(ctx, ex.config.OperationTimeout)
123129
defer cancel()
124130

125131
errsCh <- ex.createRoom(ctx, scheduler, executionLogger)

internal/core/operations/rooms/add/executor_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,89 @@ func TestExecutor_Execute(t *testing.T) {
308308

309309
require.Nil(t, err)
310310
})
311+
312+
t.Run("use default operation timeout if OperationTimeout not set", func(t *testing.T) {
313+
executor, _, _, _ := testSetup(t, Config{})
314+
require.NotNil(t, executor)
315+
require.Equal(t, executor.config.OperationTimeout, DefaultOperationTimeout)
316+
})
317+
318+
t.Run("use default operation timeout if invalid OperationTimeout value", func(t *testing.T) {
319+
executor, _, _, _ := testSetup(t, Config{OperationTimeout: -1 * time.Second})
320+
require.NotNil(t, executor)
321+
require.Equal(t, executor.config.OperationTimeout, DefaultOperationTimeout)
322+
})
323+
324+
t.Run("use custom operation timeout when valid value provided", func(t *testing.T) {
325+
customTimeout := 10 * time.Second
326+
executor, _, _, _ := testSetup(t, Config{OperationTimeout: customTimeout})
327+
require.NotNil(t, executor)
328+
require.Equal(t, executor.config.OperationTimeout, customTimeout)
329+
})
330+
331+
t.Run("should timeout when room creation takes longer than configured timeout", func(t *testing.T) {
332+
shortTimeoutDefinition := Definition{Amount: 1}
333+
shortTimeoutConfig := Config{
334+
AmountLimit: 10,
335+
BatchSize: 10,
336+
OperationTimeout: 10 * time.Millisecond, // Very short timeout
337+
}
338+
339+
_, roomsManager, schedulerStorage, operationsManager := testSetup(t, shortTimeoutConfig)
340+
341+
schedulerStorage.EXPECT().GetScheduler(context.Background(), op.SchedulerName).Return(&scheduler, nil)
342+
343+
// Mock room creation to take longer than the timeout
344+
roomsManager.EXPECT().CreateRoom(gomock.Any(), gomock.Any(), false).DoAndReturn(
345+
func(ctx context.Context, scheduler entities.Scheduler, checkRoomDeletion bool) (*game_room.GameRoom, *game_room.Instance, error) {
346+
select {
347+
// Sleep longer than the timeout to trigger context deadline
348+
case <-time.After(100 * time.Millisecond):
349+
return &gameRoom, &gameRoomInstance, nil
350+
// Should return context deadline exceeded
351+
case <-ctx.Done():
352+
return nil, nil, ctx.Err()
353+
}
354+
},
355+
).Times(1)
356+
357+
operationsManager.EXPECT().AppendOperationEventToExecutionHistory(gomock.Any(), &op, "more rooms failed than succeeded, errors: 1 and successes: 0 of amount: 1")
358+
359+
executor := NewExecutor(roomsManager, schedulerStorage, operationsManager, shortTimeoutConfig)
360+
err := executor.Execute(context.Background(), &op, &shortTimeoutDefinition)
361+
362+
require.NotNil(t, err)
363+
require.ErrorContains(t, err, "more rooms failed than succeeded")
364+
})
365+
366+
t.Run("should succeed when room creation completes within configured timeout", func(t *testing.T) {
367+
fastDefinition := Definition{Amount: 2}
368+
reasonableTimeoutConfig := Config{
369+
AmountLimit: 10,
370+
BatchSize: 10,
371+
OperationTimeout: 1 * time.Second, // Reasonable timeout
372+
}
373+
374+
_, roomsManager, schedulerStorage, operationsManager := testSetup(t, reasonableTimeoutConfig)
375+
376+
schedulerStorage.EXPECT().GetScheduler(context.Background(), op.SchedulerName).Return(&scheduler, nil)
377+
378+
// Mock room creation to complete quickly within the timeout
379+
roomsManager.EXPECT().CreateRoom(gomock.Any(), gomock.Any(), false).DoAndReturn(
380+
func(ctx context.Context, scheduler entities.Scheduler, checkRoomDeletion bool) (*game_room.GameRoom, *game_room.Instance, error) {
381+
// Complete quickly within timeout
382+
time.Sleep(50 * time.Millisecond) // Much less than 1 second
383+
return &gameRoom, &gameRoomInstance, nil
384+
},
385+
).Times(2)
386+
387+
operationsManager.EXPECT().AppendOperationEventToExecutionHistory(gomock.Any(), &op, fmt.Sprintf("added %d rooms", fastDefinition.Amount))
388+
389+
executor := NewExecutor(roomsManager, schedulerStorage, operationsManager, reasonableTimeoutConfig)
390+
err := executor.Execute(context.Background(), &op, &fastDefinition)
391+
392+
require.Nil(t, err)
393+
})
311394
}
312395

313396
func TestExecutor_Rollback(t *testing.T) {

internal/service/config.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,18 @@ import (
4040
)
4141

4242
const (
43-
healthControllerExecutionIntervalConfigPath = "workers.healthControllerInterval"
44-
storagecleanupExecutionIntervalConfigPath = "workers.storageClenupInterval"
45-
roomInitializationTimeoutMillisConfigPath = "services.roomManager.roomInitializationTimeoutMillis"
46-
roomRoomValidationAttemptsConfigPath = "services.roomManager.roomValidationAttempts"
47-
roomPingTimeoutMillisConfigPath = "services.roomManager.roomPingTimeoutMillis"
48-
roomDeletionTimeoutMillisConfigPath = "services.roomManager.roomDeletionTimeoutMillis"
49-
operationLeaseTTLMillisConfigPath = "services.operationManager.operationLeaseTTLMillis"
50-
schedulerCacheTTLMillisConfigPath = "services.eventsForwarder.schedulerCacheTTLMillis"
51-
operationsRoomsAddLimitConfigPath = "operations.rooms.add.limit"
52-
operationsRoomsAddBatchSizePath = "operations.rooms.add.batchSize"
53-
operationsRoomsRemoveLimitConfigPath = "operations.rooms.remove.limit"
43+
healthControllerExecutionIntervalConfigPath = "workers.healthControllerInterval"
44+
storagecleanupExecutionIntervalConfigPath = "workers.storageClenupInterval"
45+
roomInitializationTimeoutMillisConfigPath = "services.roomManager.roomInitializationTimeoutMillis"
46+
roomRoomValidationAttemptsConfigPath = "services.roomManager.roomValidationAttempts"
47+
roomPingTimeoutMillisConfigPath = "services.roomManager.roomPingTimeoutMillis"
48+
roomDeletionTimeoutMillisConfigPath = "services.roomManager.roomDeletionTimeoutMillis"
49+
operationLeaseTTLMillisConfigPath = "services.operationManager.operationLeaseTTLMillis"
50+
schedulerCacheTTLMillisConfigPath = "services.eventsForwarder.schedulerCacheTTLMillis"
51+
operationsRoomsAddLimitConfigPath = "operations.rooms.add.limit"
52+
operationsRoomsAddBatchSizePath = "operations.rooms.add.batchSize"
53+
operationsRoomsAddOperationTimeoutConfigPath = "operations.rooms.add.operationTimeout"
54+
operationsRoomsRemoveLimitConfigPath = "operations.rooms.remove.limit"
5455
)
5556

5657
// NewCreateSchedulerVersionConfig instantiate a new CreateSchedulerVersionConfig to be used by the NewSchedulerVersion operation to customize its configuration.
@@ -88,10 +89,12 @@ func NewHealthControllerConfig(c config.Config) healthcontroller.Config {
8889
func NewOperationRoomsAddConfig(c config.Config) add.Config {
8990
operationsRoomsAddLimit := int32(c.GetInt(operationsRoomsAddLimitConfigPath))
9091
operationsRoomsAddBatchSize := int32(c.GetInt(operationsRoomsAddBatchSizePath))
92+
operationsRoomsAddOperationTimeout := c.GetDuration(operationsRoomsAddOperationTimeoutConfigPath)
9193

9294
config := add.Config{
93-
AmountLimit: operationsRoomsAddLimit,
94-
BatchSize: operationsRoomsAddBatchSize,
95+
AmountLimit: operationsRoomsAddLimit,
96+
BatchSize: operationsRoomsAddBatchSize,
97+
OperationTimeout: operationsRoomsAddOperationTimeout,
9598
}
9699

97100
return config

0 commit comments

Comments
 (0)