Context Propagation in Go: A Distributed Task Manager
Context propagation is a crucial pattern in distributed systems and concurrent Go programs. This challenge asks you to implement a simplified context propagation mechanism to track request IDs across multiple functions, simulating a distributed task manager where each function represents a microservice or a stage in a processing pipeline. This allows for easier debugging and tracing of requests as they flow through the system.
Problem Description
You are tasked with creating a ContextPropagator that allows you to associate a request ID with a context.Context and propagate that ID to subsequent functions. The ContextPropagator should provide the following functionalities:
-
WithRequestID(ctx context.Context, requestID string) context.Context: This function takes an existingcontext.Contextand arequestID(a string) and returns a newcontext.Contextwith therequestIDassociated with it. TherequestIDshould be stored in a key named "request-id" within the context. -
GetRequestID(ctx context.Context) (string, bool): This function takes acontext.Contextand attempts to retrieve therequestIDassociated with it. It should return therequestIDas a string and a boolean indicating whether therequestIDwas found in the context. If therequestIDis not present, it should return an empty string andfalse.
You will also need to implement two example functions, processStage1 and processStage2, that demonstrate the propagation of the request ID using your ContextPropagator. processStage1 should take a context and a request ID, add the request ID to the context, and then call processStage2 with the modified context. processStage2 should retrieve the request ID from the context and print it.
Examples
Example 1:
Input:
- Initial context: context.Background()
- requestID: "12345"
Output:
- processStage2 output: "Request ID: 12345"
Explanation:
- processStage1 receives the initial context and "12345".
- processStage1 creates a new context with the request ID "12345" associated with the "request-id" key.
- processStage1 calls processStage2 with the new context.
- processStage2 retrieves the request ID "12345" from the context and prints it.
Example 2:
Input:
- Initial context: context.Background()
- requestID: "" (empty string)
Output:
- processStage2 output: "Request ID: " (empty string)
Explanation:
- processStage1 receives the initial context and an empty string.
- processStage1 creates a new context with an empty string associated with the "request-id" key.
- processStage1 calls processStage2 with the new context.
- processStage2 retrieves the empty string from the context and prints it.
Example 3: (Edge Case - No Request ID)
Input:
- Initial context: context.Background()
Output:
- processStage2 output: "Request ID: " (empty string)
Explanation:
- processStage1 receives the initial context and no request ID.
- processStage1 creates a new context with no request ID associated with the "request-id" key.
- processStage1 calls processStage2 with the new context.
- processStage2 attempts to retrieve the request ID from the context, finds nothing, and returns an empty string.
Constraints
- The
requestIDwill be a string. - The context key for the request ID must be "request-id".
- The solution must be implemented in Go.
- The solution should be efficient and avoid unnecessary memory allocations.
Notes
- Consider using
context.WithValueto associate the request ID with the context. - Remember to handle the case where the request ID is not present in the context gracefully.
- The focus is on the context propagation mechanism itself, not on the actual processing logic within
processStage1andprocessStage2. They are simply placeholders to demonstrate the propagation. - Error handling is not required for this challenge. The focus is on the context propagation logic.
- The
context.Contextis immutable. You must create a new context when adding a value.