diff --git a/micro/service.go b/micro/service.go index 7a74b0b9a..d6a8c82f4 100644 --- a/micro/service.go +++ b/micro/service.go @@ -434,6 +434,7 @@ func addEndpoint(s *service, name, subject string, handler Handler, metadata map if err != nil { return err } + s.m.Lock() endpoint.subscription = sub s.endpoints = append(s.endpoints, endpoint) endpoint.stats = EndpointStats{ @@ -441,6 +442,7 @@ func addEndpoint(s *service, name, subject string, handler Handler, metadata map Subject: subject, QueueGroup: queueGroup, } + s.m.Unlock() return nil } @@ -697,6 +699,9 @@ func (s *service) serviceIdentity() ServiceIdentity { // Info returns information about the service func (s *service) Info() Info { + s.m.Lock() + defer s.m.Unlock() + endpoints := make([]EndpointInfo, 0, len(s.endpoints)) for _, e := range s.endpoints { endpoints = append(endpoints, EndpointInfo{ diff --git a/micro/test/service_test.go b/micro/test/service_test.go index d697c5906..ea49f6c9b 100644 --- a/micro/test/service_test.go +++ b/micro/test/service_test.go @@ -188,7 +188,6 @@ func TestServiceBasics(t *testing.T) { if svcs[0].Stats().Endpoints[0].NumRequests != 0 { t.Fatalf("Expected empty stats after reset; got: %+v", svcs[0].Stats()) } - } func TestAddService(t *testing.T) { @@ -1016,7 +1015,48 @@ func TestContextHandler(t *testing.T) { if resp.Header.Get(micro.ErrorCodeHeader) != "400" { t.Fatalf("Expected error response after canceling context; got: %q", string(resp.Data)) } +} + +func TestAddEndpoint_RaceCondition(t *testing.T) { + // This test will fail with the '-race' flag if the lock/unlock are removed from service.go lines 437 and 445 + s := RunServerOnPort(-1) + defer s.Shutdown() + nc, err := nats.Connect(s.ClientURL()) + if err != nil { + t.Fatalf("Expected to connect to server, got %v", err) + } + defer nc.Close() + + ctx := context.Background() + + handler := func(ctx context.Context, req micro.Request) { + req.RespondJSON(map[string]any{"hello": "world"}) + } + config := micro.Config{ + Name: "test_service", + Version: "0.1.0", + Endpoint: µ.EndpointConfig{ + Subject: "test.func", + Handler: micro.ContextHandler(ctx, handler), + }, + } + + srv, err := micro.AddService(nc, config) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + defer srv.Stop() + + errs := make(chan error) + go func(errs chan error) { + errs <- srv.AddEndpoint("test", micro.ContextHandler(ctx, handler)) + }(errs) + + err = <-errs + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } } func TestServiceStats(t *testing.T) {