Skip to content

[gin-dynamodb-valkey] demonstrate ctx propagation, SQS poller, outbound HTTP#182

Open
karthikeyangs9 wants to merge 2 commits into
mainfrom
fix/gin-ctx-propagation-spans
Open

[gin-dynamodb-valkey] demonstrate ctx propagation, SQS poller, outbound HTTP#182
karthikeyangs9 wants to merge 2 commits into
mainfrom
fix/gin-ctx-propagation-spans

Conversation

@karthikeyangs9
Copy link
Copy Markdown
Contributor

Summary

Refactor the gin-dynamodb-valkey example to demonstrate the patterns required for spans to link into one trace per request. Came out of debugging a customer (dev-quests-service) whose traces were producing single-span orphans even though agent.Start, ginagent.Middleware, and otelaws were all wired correctly — context.Context was not threaded through service / repository layers.

Changes

  • Service struct holds clients only; methods take ctx context.Context as first arg. No struct-cached ctx — that's the most common cause of orphan span trees in real customer code.
  • Custom internal spans via tracer.Start(ctx, "Service.X") so handler-side business logic appears as children of the gin SERVER span.
  • Outbound HTTP via httpagent.NewClient + http.NewRequestWithContext — emits CLIENT spans nested under ctx and injects W3C traceparent for cross-service trace continuation. Bare http.Client orphans spans AND breaks downstream traces.
  • SQS long-poll worker with trace.WithNewRoot() per iteration and a long-lived ctx (signal.NotifyContext on SIGINT/SIGTERM) so polls aren't cancelled mid-flight and recorded as STATUS_CODE_ERROR.
  • Graceful shutdown via http.Server.Shutdown.
  • README adds a Context Propagation note linking to the product integration doc.
  • Removed stale go.mod.new.

Companion product-doc PR: last9/product-integrations#fix/golang-gin-ctx-propagation

Test plan

  • gofmt -l . clean
  • go vet ./... clean
  • go build ./... clean
  • docker compose up -d + create+seed table + go run . boots
  • curl /users/u1 produces a trace with SERVER → Service.GetUserDynamoDB.GetItem linked
  • curl /external?url=https://httpbin.org/get produces SERVER → Service.CallExternalHTTP GET linked, with traceparent header sent
  • With SQS_QUEUE_URL set: poller emits sqs.poll root spans with process.message children; no STATUS_CODE_ERROR on empty queues

…nd HTTP

Refactor example to show the patterns required for spans to link into one
trace per request:

- Service struct holds clients only; methods take ctx context.Context as
  first arg (no struct-cached ctx — the most common cause of orphan span
  trees in real customer code).
- Custom internal spans via tracer.Start(ctx, "Service.X") so handler-side
  business logic appears as children of the gin SERVER span.
- Outbound HTTP via httpagent.NewClient + http.NewRequestWithContext —
  emits CLIENT spans nested under ctx AND injects W3C traceparent for
  cross-service trace continuation.
- SQS long-poll worker with trace.WithNewRoot() per iteration and a
  long-lived ctx (signal.NotifyContext on SIGINT/SIGTERM) so polls aren't
  cancelled mid-flight and recorded as STATUS_CODE_ERROR.
- Graceful shutdown via http.Server.Shutdown.

README adds a Context Propagation note linking to the product integration
doc for the full pattern guide. Removed stale go.mod.new.
Comment thread go/gin-dynamodb-valkey/main.go Fixed
…CodeQL #58)

CodeQL flagged the /external handler for SSRF — the request URL came from
the `?url=` query parameter, which is user-controlled. The endpoint exists
purely to demonstrate httpagent.NewClient + ctx propagation, so the URL
never needed to be configurable. Hardcoding to httpbin.org/get removes the
attack surface without changing what the example teaches.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants