package main
import (
"time"
"google.golang.org/grpc"
)
func ShutdownGRPC(s *grpc.Server, timeout time.Duration) {
done := make(chan struct{})
go func() {
s.GracefulStop()
close(done)
}()
t := time.NewTimer(timeout)
defer t.Stop()
select {
case <-done:
return
case <-t.C:
s.Stop()
return
}
}
For gRPC services, GracefulStop is ideal because it allows in-flight RPCs to finish, but it can hang if handlers ignore cancellation or if clients never close streams. I wrap shutdown in a deadline: call GracefulStop in a goroutine, and if it doesn’t return in time, call Stop to force-close connections. The important operational habit is consistency: on deploy, you want the instance to drain quickly and predictably. I also use a root context canceled by signals so handlers can return promptly. This pattern prevents the “pods stuck terminating” problem and avoids dropping requests mid-flight unnecessarily. In production I log shutdown start/end and export a gauge for in-flight requests so you can see whether the service drains cleanly.
Related snips
package api
import (
"net/http"
"runtime/debug"
)
Expose build metadata for debugging deploys
export interface RetryOptions {
retries: number;
baseMs: number;
maxMs: number;
signal?: AbortSignal;
onRetry?: (attempt: number, delay: number, err: unknown) => void;
Exponential backoff with jitter for retries
package dbutil
import (
"context"
"github.com/jackc/pgconn"
Retry Postgres serialization failures with bounded attempts
package files
import (
"context"
"time"
Presigned S3 upload URLs (AWS SDK v2)
package deps
import (
"net"
"net/http"
"time"
HTTP client tuned for production: timeouts, transport, and connection reuse
package api
import (
"io"
"net/http"
"os"
Safe multipart uploads using temp files (bounded memory)
Share this code
Here's the card — post it anywhere.