Skip to content

πŸ› [Bug]: Three goroutine leaks β€” background goroutines with no shutdown mechanismΒ #4330

@pageton

Description

@pageton

Bug Description

Three background goroutines use infinite loops (for range ticker.C or for {}) with no stop mechanism. Once spawned, they can never be terminated. The defer ticker.Stop() calls are dead code because the goroutines never exit.

How to Reproduce

Steps to reproduce the behavior:

  1. Create a Fiber app with logger middleware (containing ${time}) and cache middleware
  2. Run go test -count=5 ./... or use goleak to detect leaked goroutines
  3. Observe leaked goroutine count increases with each middleware instantiation
  4. In tests that create/teardown Fiber apps, goroutines accumulate indefinitely

Affected Goroutines

1. Logger timestamp updater β€” middleware/logger/logger.go:43-48

go func() {
    for {
        time.Sleep(cfg.TimeInterval)
        timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat))
    }
}()

No context, no done channel, no way to stop.

2. Cache timestamp updater β€” middleware/cache/cache.go:173-179

go func() {
    ticker := time.NewTicker(timestampUpdatePeriod)
    defer ticker.Stop()  // dead code β€” goroutine never exits
    for range ticker.C {
        atomic.StoreUint64(&timestamp, safeUnixSeconds(time.Now()))
    }
}()

3. internal/memory GC β€” internal/memory/memory.go:110-141

func (s *Storage) gc(sleep time.Duration) {
    ticker := time.NewTicker(sleep)
    defer ticker.Stop()  // dead code β€” goroutine never exits
    for range ticker.C {
        // ... cleanup expired entries ...
    }
}

Storage has no Close() method and no done channel.

Expected Behavior

All three should follow the pattern already used in internal/storage/memory/memory.go:

func (s *Storage) gc() {
    ticker := time.NewTicker(s.gcInterval)
    defer ticker.Stop()
    for {
        select {
        case <-s.done:    // ← proper shutdown signal
            return
        case <-ticker.C:
            // ...
        }
    }
}

Each needs a done chan struct{} field and a Close() or Stop() method. The middleware constructors should expose cleanup or accept a context.

Fiber Version

v3 (latest main branch)

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

Status
No status

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions