Context Management for Thread and Trace Entities
This document describes the implementation of thread and trace entity management in the context system.
Overview
Thread and Trace entities have been added to the context management system, allowing them to be stored and retrieved throughout the request lifecycle.
Components
1. Context Methods
Thread Context (thread.go)
WithThread(ctx, thread)- Stores a thread entity in the contextGetThread(ctx)- Retrieves the thread entity from the context
Trace Context (trace.go)
WithTrace(ctx, trace)- Stores a trace entity in the contextGetTrace(ctx)- Retrieves the trace entity from the context
2. Business Logic Layer
ThreadService (internal/server/biz/thread.go)
GetOrCreateThread(ctx, threadID, projectID)- Gets an existing thread or creates a new oneGetThreadByID(ctx, threadID, projectID)- Retrieves a thread by its ID
TraceService (internal/server/biz/trace.go)
GetOrCreateTrace(ctx, traceID, projectID, threadID)- Gets an existing trace or creates a new oneGetTraceByID(ctx, traceID, projectID)- Retrieves a trace by its ID
3. Middleware
Thread Middleware (internal/server/middleware/thread.go)
WithThreadID(threadService)- ExtractsX-Thread-IDheader and gets/creates the thread entity- Requires project ID in context
- Non-blocking: continues on error
Trace Middleware (internal/server/middleware/trace.go)
WithTraceID(traceService)- ExtractsX-Trace-IDheader and gets/creates the trace entity- Requires project ID in context
- Optionally links to thread if available in context
- Non-blocking: continues on error
Usage
Setting up Middleware
// Initialize services
threadService := biz.NewThreadService()
traceService := biz.NewTraceService()
// Add middleware to router (order matters)
router.Use(middleware.WithProjectID()) // Must come first
router.Use(middleware.WithThreadID(threadService))
router.Use(middleware.WithTraceID(traceService))
Using in Handlers
func MyHandler(c *gin.Context) {
// Get thread from context
if thread, ok := contexts.GetThread(c.Request.Context()); ok {
// Use thread
fmt.Printf("Thread ID: %s\n", thread.ThreadID)
}
// Get trace from context
if trace, ok := contexts.GetTrace(c.Request.Context()); ok {
// Use trace
fmt.Printf("Trace ID: %s\n", trace.TraceID)
if trace.ThreadID != 0 {
fmt.Printf("Linked to Thread: %d\n", trace.ThreadID)
}
}
}
Client Usage
Clients can send thread and trace IDs via headers:
curl -H "X-Project-ID: proj_123" \
-H "X-Thread-ID: thread-abc-123" \
-H "X-Trace-ID: trace-xyz-456" \
https://api.example.com/endpoint
Important Notes
Global Uniqueness: Both
thread_idandtrace_idare globally unique across all projects (enforced by database schema)Project Requirement: Both middleware require a project ID in the context. If no project ID is present, they skip entity creation.
Non-blocking: Both middleware are non-blocking - if entity creation fails, the request continues without the entity in context.
Thread-Trace Relationship: Traces can optionally be linked to threads. If a thread is in the context when a trace is created, the trace will be automatically linked to it.
Idempotency: The
GetOrCreatemethods are idempotent - calling them multiple times with the same ID returns the same entity.
Testing
Comprehensive test coverage includes:
- Context storage and retrieval tests
- Business logic tests with database operations
- Middleware integration tests
- Idempotency tests
- Error handling tests
Run tests:
go test ./internal/contexts/
go test ./internal/server/biz/thread_test.go ./internal/server/biz/thread.go
go test ./internal/server/biz/trace_test.go ./internal/server/biz/trace.go
go test ./internal/server/middleware/thread_test.go ./internal/server/middleware/thread.go
go test ./internal/server/middleware/trace_test.go ./internal/server/middleware/trace.go