Skip to content

[PipeCD-Server] Server should not crash when panic occurs #6612

@armistcxy

Description

@armistcxy

What would you like to be added: An interceptor to help server recovery from panic

Why is this needed: pipecd server crashes and restarts when panic occurs, we should avoid this happens in production environment

There are 2 ways to do this:

  1. directly use https://github.com/grpc-ecosystem/go-grpc-middleware/blob/v1.4.0/recovery/interceptors.go
  2. implement by our own to avoid dependency
// PanicRecoveryUnaryServerInterceptor returns a gRPC unary interceptor that
// recovers from panics in request handlers to prevent the server from crashing.
func PanicRecoveryUnaryServerInterceptor(logger *zap.Logger) grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) {
		panicked := true
		defer func() {
			if r := recover(); r != nil || panicked {
				var panicErr error
				switch v := r.(type) {
				case error:
					panicErr = v
				case nil:
					panicErr = fmt.Errorf("runtime.Goexit() called")
				default:
					panicErr = fmt.Errorf("%v", v)
				}

				logger.Error("panic recovered in gRPC handler",
					zap.String("method", info.FullMethod),
					zap.Error(panicErr),
					zap.Stack("stack"),
				)

				err = status.Error(codes.Internal, "internal server error")
			}
		}()
		resp, err := handler(ctx, req)
		panicked = false
		return resp, err
	}
}

Explain a little bit for the usage of panicked: when panic occurs, recover() returns the value of panic (panic != nil) but when Goexit, recover() return nil -> the condition r != nil won't detect it

panicked flag helps us distinguishing "handler returns normally" or "handler is terminated by Goexit" (this technique I learn from https://github.com/grpc-ecosystem/go-grpc-middleware/blob/v1.4.0/recovery/interceptors.go)

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions