Sobes.tech
Senior

The service makes requests to an external API that has started responding in 10 seconds. Goroutines are piling up, memory is growing, and the service crashes due to OOM. How to protect?

sobes.tech AI

Answer from AI

The problem is that goroutines are blocked on long requests to external APIs, leading to an accumulation of goroutines and increased memory consumption.

How to protect the service:

  1. Limit the number of concurrently executing requests — use a semaphore or a worker pool to avoid creating too many goroutines.

  2. Set timeouts on HTTP requests — use a context with a timeout (context.WithTimeout) or configure timeouts in the HTTP client so that requests do not hang indefinitely.

  3. Handle errors and cancel requests on timeout — so goroutines do not block.

  4. Use context for request cancellation — pass context.Context to functions to be able to cancel requests if necessary.

Example with timeout and concurrency limit:

var semaphore = make(chan struct{}, 10) // maximum 10 concurrent requests

func callExternalAPI(ctx context.Context, url string) error {
    semaphore <- struct{}{}         // acquire slot
    defer func() { <-semaphore }() // release slot

    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()

    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    if err != nil {
        return err
    }

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    // process response
    return nil
}

This approach prevents goroutine accumulation and OOM crashes.

The service makes requests to an external API that… - sobes.tech