Skip to content

plug file-descriptor leaks#32

Open
proditis wants to merge 3 commits intomainfrom
fd-leak-fixes
Open

plug file-descriptor leaks#32
proditis wants to merge 3 commits intomainfrom
fd-leak-fixes

Conversation

@proditis
Copy link
Contributor

@proditis proditis commented Feb 28, 2026

  • FIX 1: unregisterConnection never called conn.Close() (primary leak).
    The map entry was deleted but the underlying TCP socket was never closed. Each time a user navigated to a new page the old conn was removed from the map but its fd stayed open. This is the main culprit for the 3-4 hour exhaustion window.

  • FIX 2: TOCTOU race on connection limit check.
    The count check and registration were not atomic. Under rapid reconnects (exactly your use case) multiple goroutines could pass the limit simultaneously, allowing more connections than intended and wasting fds.

  • FIX 3: defer unregisterConnection is now the single fd-release point.
    Since unregisterConnection now calls conn.Close(), the deferred call in wsHandler is the single authoritative cleanup path regardless of how the handler exits.

  • FIX 4: Ping goroutine leaked on every disconnect (secondary leak).
    for range ticker.C has no exit condition. ticker.Stop() prevents new ticks but the goroutine blocks on the channel forever, keeping a reference to conn alive. Replaced with a select on a done channel that is closed when the handler returns.

  • FIX 5: startTokenRevalidation ticker never stopped.
    Refactored to accept a stopCh <-chan struct{} and defer ticker.Stop() so the goroutine exits cleanly on shutdown.

  • FIX 6: Log file fd never closed.
    setupLogging opened the file but had no way to close it. The handle is now stored in logFileHandle and deferred-closed in run().

  • FIX 7: db.Close() never called.
    The database connection pool holds fds. defer db.Close() added in run().

  • FIX 8: Offline cleanup goroutine ticker never stopped.
    Same pattern as FIX 5 converted to select on stopCh.

  • FIX 9: Reduce code complexity for func run()

@proditis proditis self-assigned this Feb 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant