Session activity interceptor with strip-on-consume#233
Session activity interceptor with strip-on-consume#233rajkannan-carousell merged 19 commits intomasterfrom
Conversation
🔴 CRITICAL FindingsC1. The diff adds both Fix: Remove the |
🟠 HIGH FindingsH1. Goroutine leak when Kafka is unavailable —
Since the interceptor launches Fix options:
H2. Excessive Info-level logging per tracked request — 4 Info-level log lines fire per tracked request:
Since Orion is a shared framework imported by many services, these logs affect ALL adopting services. Under moderate traffic with session tracking enabled, this will flood log aggregation. Fix: Move these to Debug level. Keep Error logs for failures only. H3. Major dependency version bumps bundled with feature PR — gRPC bumped from Recommendation: Consider splitting dependency bumps into a separate PR. Verify gRPC v1.76 compatibility with consuming services (api-gw, user-svc, auth-svc at minimum) before merging. |
🧪 Test Suggestions🟠 HIGH T1. Test that when |
- Remove overly broad *.md gitignore entry that blocked all markdown files - Move per-request session tracking logs from Info to Debug level - Keep timeout log at Error level for operational visibility
Use context.WithTimeout in PublishAsync instead of goroutine + time.After. Produce now uses select on ctx.Done() so the goroutine exits when the context expires, preventing accumulation under Kafka backpressure.
What is this?
Session activity tracking at the gRPC interceptor layer — publishes a structured Kafka event (service, action, status, duration, encoded session context) whenever a gateway-initiated, security-sensitive operation completes (e.g. ChangePassword, UpdateProfile, Login).
Why interceptor level, not application code?
Interceptors run for every gRPC handler automatically, capturing the real completion status and duration without touching business logic. Putting this in service code would scatter event publishing across every handler and risk missing error paths.
Note: **As per latest alignement, we just publish the activity event only for opted in API's via gateway from this interceptor. Also, we don't store the activity events right now and we only store when any signal changes like device id, country as metadata history with the reference that which activity triggered. **
How It Works
The interceptor fires only when
x-session-track: trueis present. After reading the tracking headers, it strips them from incoming metadata before callinghandler(). SinceForwardMetadataInterceptorreads from incoming metadata, the stripped headers are never relayed downstream.Why strip and not just skip? Skipping wouldn't prevent forwarding —
ForwardMetadataInterceptorruns inside the handler and copies from incoming metadata. Stripping replaces the incoming context before the handler runs, so downstream calls never carry the header.Opt-In Only
This interceptor is entirely opt-in at two levels:
GlobalSessionActivityInterceptor()to its interceptor chain and registersSessionInitializer.x-session-track: true. Internal service-to-service calls never set this header (it gets stripped on first hop), so they are always silent.No gateway changes → no events. No
x-session-track→ no events. Zero noise by default.How to Onboard a Downstream Service
1. Register the initializer (wires the Kafka producer at startup):
2. Add config (viper / config file):
3. Add interceptor to your service:
4. Gateway sets the headers for operations you want tracked:
If
kafka_brokersis not configured, the initializer is a no-op and the interceptor silently drops events — startup is never blocked.Files Changed
interceptors/session_interceptor.gox-session-trackguard, strip-on-consume, operation label,GlobalSessionActivityInterceptor, nil-producer warninginterceptors/session_interceptor_test.goorion/session_initializer.goorion/session_initializer_test.goorion/config.goSessionTrackingConfigstruct + viper config key constantsTest Plan
x-session-trackabsent → handler runs, no eventx-session-track: true+x-session-contextpresent → event publishedx-session-track: true+x-session-contextempty → skipx-session-trackandx-session-operationstripped from incoming MDForwardMetadataInterceptorsimulation: stripped headers not in outgoing MDx-session-operationset →Action= label; absent →Action=info.FullMethodStatus= gRPC code stringsync.OnceGlobalSessionActivityInterceptoruses globals fromSessionInitializer