Skip to content

fix: Prevent goroutine leak in Crontab by adding graceful shutdown support#3292

Open
tharunn0 wants to merge 3 commits intogofr-dev:developmentfrom
tharunn0:fix/crontab-goroutine-leak
Open

fix: Prevent goroutine leak in Crontab by adding graceful shutdown support#3292
tharunn0 wants to merge 3 commits intogofr-dev:developmentfrom
tharunn0:fix/crontab-goroutine-leak

Conversation

@tharunn0
Copy link
Copy Markdown
Contributor

@tharunn0 tharunn0 commented Apr 7, 2026

Description:

Updates the crontab background job runner to support graceful shutdown, replacing the infinite for ... range ticker loop with a select statement that listens for a termination signal.
Previously, terminating the application would inadvertently leak the associated background goroutine because it lacked an explicit exit condition.
The changes align the crontab implementation with the framework's graceful shutdown lifecycle by integrating the new Crontab.Stop() method into App.Shutdown.

Fixes goroutine leak during crontab shutdown

Breaking Changes (if applicable):

  • None

Additional Information:

  • None

Checklist:

  • I have formatted my code using goimport and golangci-lint.
  • All new code is covered by unit tests.
  • This PR does not decrease the overall code coverage.
  • I have reviewed the code comments and documentation for clarity.

Fixes #3211

Copy link
Copy Markdown
Member

@Umang01-hash Umang01-hash left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tharunn0 Thanks for the effort to make GoFr better. Here are a few things that should be fixed before we go ahead with the PR:

Issue 1 : Double-Stop panics

close(c.done) will panic if Stop() is called twice. While ticker.Stop() is idempotent, close() on an already-closed channel is not. Use sync.Once:

type Crontab struct {
    // ...
    done     chan struct{}
    stopOnce sync.Once
}

func (c *Crontab) Stop() {
    c.stopOnce.Do(func() {
        c.ticker.Stop()
        close(c.done)
    })
}

Issue 2 Shutdown ordering

cron.Stop() is placed after a.container.Close() in Shutdown(). This means datasources are closed before cron scheduling stops — a cron job could fire against closed datasources in that window. Move cron stop before container close:

if a.cron != nil {
    a.cron.Stop()
}

if a.container != nil {
    err = errors.Join(err, a.container.Close())
}

Issue 3 : Missing integration test

TestCrontab_Stop only tests standalone Stop(). Consider adding a test that verifies App.Shutdown() properly stops cron — e.g., create an App, add a cron job, call Shutdown, assert the goroutine exits.

@tharunn0 tharunn0 requested a review from Umang01-hash April 10, 2026 15:05
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.

Goroutine leak in Crontab — no Stop/Close mechanism

2 participants