package grpcadapter import ( "context" "errors" "fmt" "net" "time" ledgerv1 "gl/gen/ledger/v1" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) type ServerConfig struct { Host string Port int ShutdownTimeout time.Duration } func Run(ctx context.Context, cfg ServerConfig, handler ledgerv1.GeneralLedgerServiceServer) error { listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)) if err != nil { return fmt.Errorf("listen for grpc: %w", err) } return runWithListener(ctx, cfg.ShutdownTimeout, listener, handler) } func runWithListener(ctx context.Context, shutdownTimeout time.Duration, listener net.Listener, handler ledgerv1.GeneralLedgerServiceServer) error { defer listener.Close() server := grpc.NewServer() ledgerv1.RegisterGeneralLedgerServiceServer(server, handler) reflection.Register(server) serveErr := make(chan error, 1) go func() { serveErr <- server.Serve(listener) }() select { case err := <-serveErr: if errors.Is(err, grpc.ErrServerStopped) { return nil } return fmt.Errorf("serve grpc: %w", err) case <-ctx.Done(): } stopped := make(chan struct{}) go func() { server.GracefulStop() close(stopped) }() timer := time.NewTimer(shutdownTimeout) defer timer.Stop() select { case <-stopped: case <-timer.C: server.Stop() <-stopped } err := <-serveErr if err != nil && !errors.Is(err, grpc.ErrServerStopped) { return fmt.Errorf("serve grpc: %w", err) } return nil }