go-grpc-middleware icon indicating copy to clipboard operation
go-grpc-middleware copied to clipboard

Need help thinking through and event-publishing pattern with middleware

Open bryanmcgrane opened this issue 4 years ago • 1 comments

Hello!

I'm working on an event publishing middleware that can asynchronously publish events of certain types after a streaming or unary request is completed. The two middlewares look something like this:

func StreamServerInterceptor(routeMap *route_service.RouteMap, eventService event_service.IUsageEventCreator) grpc.StreamServerInterceptor {
	return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
		onComplete, err := EventMiddlewareFunction(stream.Context(), routeMap, eventService)
		if err != nil {
			return err
		}
		defer func() { go onComplete() }()
		return handler(srv, stream)
	}
}

func UnaryServerInterceptor(routeMap *route_service.RouteMap, eventService event_service.IUsageEventCreator) grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
		onComplete, err := EventMiddlewareFunction(ctx, routeMap, eventService)
		if err != nil {
			return nil, err
		}
		defer func() { go onComplete() }()
		return handler(ctx, req)
	}
}

Where my routeMap is a map of grpc routes to event types. My EventMiddlewareFunction looks something like this:

func EventMiddlewareFunction(ctx context.Context, routeMap *route_service.RouteMap, eventService event_service.IUsageEventCreator) (func(), error) {
	fullMethod, ok := grpc.Method(ctx)
	if !ok {
		return func() {}, error_util.Error(codes.Unknown, "Method could not be found given the context")
	}

	routeDefinition, exists := (*routeMap)[fullMethod]
	if !exists {
		return func() {}, error_util.Error(codes.Unimplemented, fmt.Sprintf("Route %s is not defined in routeMap", fullMethod))
	}

	// If no events are configured for this route, exit gracefully
	if len(routeDefinition.Events) == 0 {
		return func() {}, nil
	}
	// Start timer
	start := time.Now()

	// Return callback function to end timer and upload events
	return func() {
		duration := time.Since(start).Milliseconds()
		for _, eventType := range routeDefinition.Events {
			eventService.CreateUsageEvent("", start, duration, eventType, fullMethod)
		}
	}, nil
}

Now, this works great for routes where the event types are static regardless of user input. However, I have a request to dynamically change the event types based on user input for specific routes. Something like this:

func (s *ServerServiceServer) GetConfig(ctx context.Context, request *management_api_v1.ServerConfigRequest) (*management_api_v1.ServerConfig, error) {
	if request.GetSomethingHere() {
		// ADD an event type to be published
	}
}

TLDR; The main issue is, there is really no way to me to pass information back up the middleware stack from within the endpoint itself. This causes issues with patterns where something needs to happen after the request is complete, and what happens is dependent upon conditions trigged within the endpoint itself.

Any help would be greatly appreciated!

bryanmcgrane avatar Aug 27 '21 18:08 bryanmcgrane

TLDR; The main issue is, there is really no way to me to pass information back up the middleware stack from within the endpoint itself.

Hm, why do you need this?

Essentially you want to send event in middleware, right?

  1. Why not parsing response/request/message in middleware and base decision on this?
  2. Other alternative is to use context to pass a "tracker" through gRPC context in middleware. Then request changes something, then tracker gives info to middleware how to send.

I would recommend the (1), since second passing things through context is a bit of anti-pattern - you can get lost easily (:

Hope that helps 🤗

Also... use v2 branch as we will move to it soon! And framework is much more clearer!

bwplotka avatar Sep 27 '21 12:09 bwplotka

Closing for now, unless you still need help - we can reopen. Thanks!

bwplotka avatar Mar 19 '23 01:03 bwplotka