cfd9c46ca8
Address three HIGH-severity issues from the memory-leak and bloat analysis (issue #56). 1. Graceful shutdown (main.go) - Replace gin's r.Run() with an explicit http.Server so the process can intercept SIGINT / SIGTERM. - On signal: drain active HTTP requests (10 s timeout), then stop SyncService, WebSocket Hub, and InstanceAccessService cleanup goroutine in order. - Ensures database connections, K8s watchers, and background loops are released cleanly on deploy or restart. 2. WebSocket Hub init race (websocket_service.go) - GetHub() used a bare nil-check with no synchronisation; two goroutines could each create a Hub and start a Run() loop. - Replaced with sync.Once to guarantee exactly one Hub instance. - Added a stop channel to Hub.Run() so the hub can be shut down gracefully, closing all connected clients. 3. InstanceAccessService goroutine leak (instance_access_service.go) - cleanupExpiredTokens() looped on ticker.C with no exit path, leaking the goroutine for the lifetime of the process. - Added a stopChan; cleanupExpiredTokens now selects on both the ticker and the stop signal. - Exposed Stop() on the service; InstanceHandler.Shutdown() calls it during graceful shutdown. Tests: - TestGetHubReturnsSameInstance: singleton guarantee - TestGetHubConcurrentAccess: 50-goroutine race test - TestHubStopClosesClients: verifies client cleanup on Stop() - TestInstanceAccessServiceStopTerminatesCleanup: Stop() is safe and the service remains functional for token ops afterward All existing tests continue to pass; full project build and regression verified. Ref: #56