Skip to content

Commit c536d3d

Browse files
[ADD] cached method lookup to jRPC service
1 parent ca539be commit c536d3d

File tree

1 file changed

+33
-6
lines changed

1 file changed

+33
-6
lines changed

web/jrpc/jrpc.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//
1212
// Features:
1313
// - HTTP and WebSocket endpoint support
14-
// - Automatic method resolution and dispatch
14+
// - Automatic method resolution and dispatch with cached lookups
1515
// - Protocol Buffer JSON marshaling/unmarshaling
1616
// - Multiple streaming patterns (unary, server, client, bidirectional)
1717
// - Context enrichment with HTTP and WebSocket components
@@ -21,7 +21,7 @@
2121
// 1. Define your service in a .proto file
2222
// 2. Generate Go code using protoc with the protoc-gen-jrpc plugin
2323
// 3. Implement the generated Server interface
24-
// 4. Create a new jRPC service with jrpc.New(yourServer)
24+
// 4. Create a new jRPC service with jrpc.Register(yourServer)
2525
// 5. Register the HandlerFunc with the web package function WithJRPC
2626
//
2727
// Example:
@@ -50,7 +50,7 @@
5050
// err := web.Instance().
5151
// WithHost("localhost").
5252
// WithPort(8080).
53-
// WithJRPC(jrpc.New(&MyService{})).
53+
// WithJRPC(jrpc.Register(&MyService{})).
5454
// Start().Error
5555
// if err != nil {
5656
// log.Fatal().Err(err).Msg("server exited")
@@ -103,6 +103,7 @@ const (
103103
// protocol buffer message handling, and context enrichment.
104104
type Service struct {
105105
Server
106+
methods map[string]protoreflect.MethodDescriptor // cached method descriptors for faster lookup
106107
}
107108

108109
// Server represents a jRPC service implementation.
@@ -111,10 +112,29 @@ type Server interface {
111112
Descriptor() protoreflect.FileDescriptor
112113
}
113114

114-
// New creates a new jrpc service instance and registers the provided
115+
// Register creates a new jrpc service instance and registers the provided
115116
// service implementation. The service implementation has to implement the Descriptor method.
116-
func New(s Server) *Service {
117-
return &Service{Server: s}
117+
// This function builds a method cache for improved lookup performance.
118+
func Register(s Server) *Service {
119+
service := &Service{
120+
Server: s,
121+
methods: make(map[string]protoreflect.MethodDescriptor),
122+
}
123+
124+
// Build the methods cache
125+
services := s.Descriptor().Services()
126+
for i := 0; i < services.Len(); i++ {
127+
serviceDesc := services.Get(i)
128+
methods := serviceDesc.Methods()
129+
for j := 0; j < methods.Len(); j++ {
130+
methodDesc := methods.Get(j)
131+
// Use fully qualified method name as key: service.method
132+
key := string(serviceDesc.Name()) + "." + string(methodDesc.Name())
133+
service.methods[key] = methodDesc
134+
}
135+
}
136+
137+
return service
118138
}
119139

120140
// SetUpgrader allows setting a custom WebSocket upgrader with specific options.
@@ -404,6 +424,13 @@ func (s *Service) call(ctx context.Context, method string, req proto.Message) (a
404424
}
405425

406426
func (s *Service) find(service, method string) (protoreflect.MethodDescriptor, error) {
427+
// Use cached method lookup for better performance
428+
key := service + "." + method
429+
if md, exists := s.methods[key]; exists {
430+
return md, nil
431+
}
432+
433+
// Fallback to dynamic lookup if not found in cache (should not happen in normal operation)
407434
sd := s.Descriptor().Services().ByName(protoreflect.Name(service))
408435
if sd == nil {
409436
return nil, apperror.NewError("service not found")

0 commit comments

Comments
 (0)