diff --git a/CHANGELOG.md b/CHANGELOG.md index fa20603b652feae53d7fee67317d65869d039111..da65fe8381cd706fd6a6b6d7effdbf03ed768c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr - Add new langTag, members and open filters to the group listing API. - Upgrade pgx to v4 for improved SQL performance. - Change RegisterLeaderboardReset runtime function signature. +- Cancel runtime context when graceful shutdown completes. ### Fixed - Ensure all members are correctly listed in party info when there are multiple concurrent successful joins. diff --git a/main.go b/main.go index 69ede7a9bafb5b514ae757bbe7214be6c72f985d..6d05ebf79f947ea68b569177787de16026488cc7 100644 --- a/main.go +++ b/main.go @@ -119,6 +119,9 @@ func main() { db, dbVersion := dbConnect(startupLogger, config) startupLogger.Info("Database information", zap.String("version", dbVersion)) + // Global server context. + ctx, ctxCancelFn := context.WithCancel(context.Background()) + // Check migration status and fail fast if the schema has diverged. migrate.StartupCheck(startupLogger, db) @@ -140,7 +143,7 @@ func main() { tracker.SetMatchJoinListener(matchRegistry.Join) tracker.SetMatchLeaveListener(matchRegistry.Leave) streamManager := server.NewLocalStreamManager(config, sessionRegistry, tracker) - runtime, runtimeInfo, err := server.NewRuntime(logger, startupLogger, db, jsonpbMarshaler, jsonpbUnmarshaler, config, socialClient, leaderboardCache, leaderboardRankCache, leaderboardScheduler, sessionRegistry, sessionCache, matchRegistry, tracker, metrics, streamManager, router) + runtime, runtimeInfo, err := server.NewRuntime(ctx, logger, startupLogger, db, jsonpbMarshaler, jsonpbUnmarshaler, config, socialClient, leaderboardCache, leaderboardRankCache, leaderboardScheduler, sessionRegistry, sessionCache, matchRegistry, tracker, metrics, streamManager, router) if err != nil { startupLogger.Fatal("Failed initializing runtime modules", zap.Error(err)) } @@ -208,6 +211,9 @@ func main() { timer.Stop() } + // Signal cancellation to the global runtime context. + ctxCancelFn() + // Gracefully stop remaining server components. apiServer.Stop() consoleServer.Stop() diff --git a/server/runtime.go b/server/runtime.go index e8f51343077c357330f11e7dd751c44f4a316a18..e73969b7e16e6e3e0f40bdc379d8d2049cc9a5cc 100644 --- a/server/runtime.go +++ b/server/runtime.go @@ -564,7 +564,7 @@ func CheckRuntime(logger *zap.Logger, config Config) error { return nil } -func NewRuntime(logger, startupLogger *zap.Logger, db *sql.DB, protojsonMarshaler *protojson.MarshalOptions, protojsonUnmarshaler *protojson.UnmarshalOptions, config Config, socialClient *social.Client, leaderboardCache LeaderboardCache, leaderboardRankCache LeaderboardRankCache, leaderboardScheduler LeaderboardScheduler, sessionRegistry SessionRegistry, sessionCache SessionCache, matchRegistry MatchRegistry, tracker Tracker, metrics *Metrics, streamManager StreamManager, router MessageRouter) (*Runtime, *RuntimeInfo, error) { +func NewRuntime(ctx context.Context, logger, startupLogger *zap.Logger, db *sql.DB, protojsonMarshaler *protojson.MarshalOptions, protojsonUnmarshaler *protojson.UnmarshalOptions, config Config, socialClient *social.Client, leaderboardCache LeaderboardCache, leaderboardRankCache LeaderboardRankCache, leaderboardScheduler LeaderboardScheduler, sessionRegistry SessionRegistry, sessionCache SessionCache, matchRegistry MatchRegistry, tracker Tracker, metrics *Metrics, streamManager StreamManager, router MessageRouter) (*Runtime, *RuntimeInfo, error) { runtimeConfig := config.GetRuntime() startupLogger.Info("Initialising runtime", zap.String("path", runtimeConfig.Path)) @@ -579,7 +579,7 @@ func NewRuntime(logger, startupLogger *zap.Logger, db *sql.DB, protojsonMarshale matchProvider := NewMatchProvider() - goModules, goRPCFunctions, goBeforeRtFunctions, goAfterRtFunctions, goBeforeReqFunctions, goAfterReqFunctions, goMatchmakerMatchedFunction, goTournamentEndFunction, goTournamentResetFunction, goLeaderboardResetFunction, allEventFunctions, goMatchNamesListFn, err := NewRuntimeProviderGo(logger, startupLogger, db, protojsonMarshaler, config, socialClient, leaderboardCache, leaderboardRankCache, leaderboardScheduler, sessionRegistry, sessionCache, matchRegistry, tracker, streamManager, router, runtimeConfig.Path, paths, eventQueue, matchProvider) + goModules, goRPCFunctions, goBeforeRtFunctions, goAfterRtFunctions, goBeforeReqFunctions, goAfterReqFunctions, goMatchmakerMatchedFunction, goTournamentEndFunction, goTournamentResetFunction, goLeaderboardResetFunction, allEventFunctions, goMatchNamesListFn, err := NewRuntimeProviderGo(ctx, logger, startupLogger, db, protojsonMarshaler, config, socialClient, leaderboardCache, leaderboardRankCache, leaderboardScheduler, sessionRegistry, sessionCache, matchRegistry, tracker, streamManager, router, runtimeConfig.Path, paths, eventQueue, matchProvider) if err != nil { startupLogger.Error("Error initialising Go runtime provider", zap.Error(err)) return nil, nil, err diff --git a/server/runtime_go.go b/server/runtime_go.go index 21980df85a2ccc8bfb4980e612085641fe5b6554..e71fabb76752d41c8bc9e269f20e5f884c093a7a 100644 --- a/server/runtime_go.go +++ b/server/runtime_go.go @@ -2327,7 +2327,7 @@ func (ri *RuntimeGoInitializer) RegisterMatch(name string, fn func(ctx context.C return nil } -func NewRuntimeProviderGo(logger, startupLogger *zap.Logger, db *sql.DB, protojsonMarshaler *protojson.MarshalOptions, config Config, socialClient *social.Client, leaderboardCache LeaderboardCache, leaderboardRankCache LeaderboardRankCache, leaderboardScheduler LeaderboardScheduler, sessionRegistry SessionRegistry, sessionCache SessionCache, matchRegistry MatchRegistry, tracker Tracker, streamManager StreamManager, router MessageRouter, rootPath string, paths []string, eventQueue *RuntimeEventQueue, matchProvider *MatchProvider) ([]string, map[string]RuntimeRpcFunction, map[string]RuntimeBeforeRtFunction, map[string]RuntimeAfterRtFunction, *RuntimeBeforeReqFunctions, *RuntimeAfterReqFunctions, RuntimeMatchmakerMatchedFunction, RuntimeTournamentEndFunction, RuntimeTournamentResetFunction, RuntimeLeaderboardResetFunction, *RuntimeEventFunctions, func() []string, error) { +func NewRuntimeProviderGo(ctx context.Context, logger, startupLogger *zap.Logger, db *sql.DB, protojsonMarshaler *protojson.MarshalOptions, config Config, socialClient *social.Client, leaderboardCache LeaderboardCache, leaderboardRankCache LeaderboardRankCache, leaderboardScheduler LeaderboardScheduler, sessionRegistry SessionRegistry, sessionCache SessionCache, matchRegistry MatchRegistry, tracker Tracker, streamManager StreamManager, router MessageRouter, rootPath string, paths []string, eventQueue *RuntimeEventQueue, matchProvider *MatchProvider) ([]string, map[string]RuntimeRpcFunction, map[string]RuntimeBeforeRtFunction, map[string]RuntimeAfterRtFunction, *RuntimeBeforeReqFunctions, *RuntimeAfterReqFunctions, RuntimeMatchmakerMatchedFunction, RuntimeTournamentEndFunction, RuntimeTournamentResetFunction, RuntimeLeaderboardResetFunction, *RuntimeEventFunctions, func() []string, error) { runtimeLogger := NewRuntimeGoLogger(logger) node := config.GetName() env := config.GetRuntime().Environment @@ -2391,7 +2391,7 @@ func NewRuntimeProviderGo(logger, startupLogger *zap.Logger, db *sql.DB, protojs } // The baseline context that will be passed to all InitModule calls. - ctx := NewRuntimeGoContext(context.Background(), node, env, RuntimeExecutionModeRunOnce, nil, 0, "", "", nil, "", "", "") + ctx = NewRuntimeGoContext(ctx, node, env, RuntimeExecutionModeRunOnce, nil, 0, "", "", nil, "", "", "") startupLogger.Info("Initialising Go runtime provider", zap.String("path", rootPath)) diff --git a/server/runtime_test.go b/server/runtime_test.go index 940a4c403f59d4347ae27024e95e0f8e749dc7ca..1956594937ac26d99f8a1dacb4162a60ec63d288 100644 --- a/server/runtime_test.go +++ b/server/runtime_test.go @@ -76,7 +76,7 @@ func runtimeWithModules(t *testing.T, modules map[string]string) (*Runtime, *Run cfg := NewConfig(logger) cfg.Runtime.Path = dir - return NewRuntime(logger, logger, NewDB(t), protojsonMarshaler, protojsonUnmarshaler, cfg, nil, nil, nil, nil, nil, nil, nil, nil, metrics, nil, &DummyMessageRouter{}) + return NewRuntime(context.Background(), logger, logger, NewDB(t), protojsonMarshaler, protojsonUnmarshaler, cfg, nil, nil, nil, nil, nil, nil, nil, nil, metrics, nil, &DummyMessageRouter{}) } func TestRuntimeSampleScript(t *testing.T) {