Repository navigation
feat(natsclient): the shared NATS client, with private inboxes, micro endpoints, requests and metrics (ENG-443) - #3
Merged
Conversation
… endpoints, requests and metrics (ENG-443)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The NATS client half of ENG-443: one
natsclientpackage every Go service on the platform uses, in place of each service hand-rolling nats.go connect options.What is in it
Connect(ctx, Config, *slog.Logger, ...Option): the connect semantics of the platform'sgatewayclient.Connect, preserved: the projected token is re-read on every (re)connect (nats.UserInfoHandler), reconnect forever with nats.go's paced sweep,RetryOnFailedConnect,IgnoreAuthErrorAbort, a 20s ping interval, every connection event logged (expiry Info, disconnect/reconnect Info, closed Error), connection named after the service.FromRuntime(rt.Config)mapsNATS_URL,NATS_TOKEN_FILE,SERVICE_IDENTITYandOTEL_SERVICE_NAME.Connected()is a readiness check,Drain(ctx)the graceful stop.nats.CustomInboxPrefix("_INBOX_<ns>_<sa>"), dots escaped to_asgatewayclient.SubjectIDdoes.InboxPrefix(identity)is exported so the grant generator can derive the same name.WithSharedInbox()keeps nats.go's_INBOXprefix for an identity whose grant does not yet include the private one.Request(ctx, subject, data, hdr): refuses a context without a deadline (ErrNoDeadline), injects W3Ctraceparent(propagation.TraceContext+HeaderCarrier), wraps a client span, and reportsErrNoResponders(wrapsnats.ErrNoResponders) andErrTimeout(wrapscontext.DeadlineExceeded) distinctly. An endpoint's micro error response comes back as*ServiceError{Code, Description}.nats_requests_total{service,endpoint,outcome}(ok|no_responders|timeout|error) andnats_request_duration_secondswith the same attributes, fromotel.Meter("github.com/MetsaApp/platform-kit/natsclient"), so they land on/metricsthrough the runtime's Prometheus exporter.Service(name, version)over nats.go'smicropackage:AddEndpoint(endpoint, handler, ...EndpointOption)serves<name>.svc.<endpoint>(ADR 0006) in the queue group<name>by default (WithQueueGroupoverrides), extracts the caller's trace into a server span, and turns a returned error into a micro error response (*ServiceErrorpicks the code, anything else is 500).$SRV.PING/INFO/STATSdiscovery comes with micro.EndpointSubject(service, endpoint)keeps the subject convention in one place.natsclientsection and "Conventions for Rust and .NET" (projected token re-read on connect, connection name,_INBOX_<ns>_<sa>viaConnectOptions::custom_inbox_prefix/NatsOpts.InboxPrefix, ADR 0006 subjects, deadlines with distinct no-responders handling, W3Ctraceparent, the same metric names).Acceptance criteria
Tests run on an embedded
nats-server:TestPrivateInboxIsolation): three users with publish/subscribe permissions shaped like the auth callout's grants; identity B's subscription to A's inbox prefix is refused (permissions violation logged), A's request still gets its reply, B's own request works;TestQueueGroupSharing): twoServiceinstances with one name split a burst of 40 requests, an endpoint in another group sees all 40;TestRequestTimeoutAndNoResponders,TestRequestNeedsDeadline): a slow endpoint yieldsErrTimeout, an unserved subjectErrNoResponders, eacherrors.Is-distinguishable from the other and from the wrapped nats.go/context errors;TestTracePropagation): the handler's context carries the caller's trace id and the exported spans form caller -> client -> server;TestRequestMetrics): a manual reader asserts the counter and histogram withservice,endpointandoutcome;TestRequestServiceError,TestInboxPrefix,TestConnectNeedsIdentityForPrivateInbox.go build ./... && go vet ./... && go test -race ./...and golangci-lint v2.11.3 are clean.Deferred
gatewayclientcallers onto this package is a later PR in the platform repo._INBOX_<ns>_<sa>.>) arrives with ENG-447; until then a service connects withWithSharedInbox(). Today's auth callout grants only_INBOX.>.nats_requests_total; micro's$SRV.STATShas per-endpoint counts and latency in the meantime.Dependencies
github.com/nats-io/nats.gov1.54.0;github.com/nats-io/nats-server/v2v2.15.1 for tests only.