Skip to content

Resource Leak: Background goroutines in SQL and Redis datasources do not exit on Shutdown #2898

@MOHITKOURAV01

Description

@MOHITKOURAV01

Describe the Bug

The SQL and Redis datasource implementations spawn background goroutines for:

  • Connection retries (retryConnection, retryConnect)
  • Metrics collection (pushDBMetrics)

These goroutines run in infinite loops without monitoring a cancellation context or stop signal.

When the application shuts down (via app.Shutdown() or container.Close()), these goroutines continue to run indefinitely, resulting in:

  • Goroutine leaks — resources are never freed.
  • Log noise — the application continues logging connection errors even after shutdown.

To Reproduce

Steps to Reproduce

  1. Initialize a GoFr application with a SQL or Redis datasource configured to use an unreachable host (so the retry loop activates).
  2. Start the application.
  3. Trigger a shutdown (e.g., via SIGINT).
  4. Observe that the background retry logic continues to execute and log errors even after shutdown.

Minimal Example

package main

import (
    "time"
    "gofr.dev/pkg/gofr"
)

func main() {
    app := gofr.New()
    
    // Configure a non-existent DB to force retry loop
    app.Config.Set("DB_HOST", "unreachable-host")
    app.Config.Set("DB_DIALECT", "mysql")
    
    go func() {
        time.Sleep(5 * time.Second)
        // Simulate shutdown
        // Even after this, the retryConnection goroutine keeps running
        app.Shutdown()
    }()
    
    app.Run()
} 

Expected behavior
When container.SQL.Close() or container.Redis.Close() is called (typically during app.Shutdown()),
all associated background goroutines (retry logic, metrics pushers, etc.) should:

Terminate immediately and gracefully.

Stop retrying connections or pushing metrics.

Release resources cleanly.

.

Environments (please complete the following information):

  • OS: [macOS]
  • gofr version [Latest source from development branch]
  • go version [1.25 (as per go.mod)]

More description

  • Add a stopSignal channel (and sync.Once for safety) to the DB and Redis structs.
  • Initialize this channel in NewSQL and NewClient.
  • Close this channel in the Close() method.
  • Update retryConnection and pushDBMetrics loops to listen for <-stopSignal and exit when triggered.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinggoPull requests that update Go code

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions