summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Carr <[email protected]>2025-10-12 07:33:05 -0500
committerJeff Carr <[email protected]>2025-10-12 07:33:05 -0500
commitb31ca9e2bbd1a0110a44ff6412036b78429cc321 (patch)
tree49c751ca69a8b11446d5be1ad6087b5b699401ad
parent49f9535f1cd17f1c30b62464555b057d3daa3c66 (diff)
notes for laterv0.1.36
-rw-r--r--context.go.notes109
1 files changed, 109 insertions, 0 deletions
diff --git a/context.go.notes b/context.go.notes
new file mode 100644
index 0000000..7cbb38c
--- /dev/null
+++ b/context.go.notes
@@ -0,0 +1,109 @@
+✦ That's one of the most important questions for a modern Go developer. The context package, and the ctx variable, are
+ fundamental to writing robust, scalable, and reliable concurrent programs in Go.
+
+ At its core, a context.Context is a standard way to carry deadlines, cancellation signals, and other request-scoped
+ values across API boundaries and between goroutines.
+
+ Think of it as a "control channel" for a request or a task.
+
+ The Three Main Jobs of a context.Context
+
+ 1. Cancellation (The Most Important Job)
+
+ This is the primary reason context was created. Imagine you start a complex operation that involves multiple goroutines
+ and network calls.
+
+ * The Problem: What happens if the user who initiated the operation closes their browser or clicks "cancel"? Without a
+ context, all those background goroutines would keep running, consuming CPU, memory, and network resources, even though
+ their work is no longer needed. This is a resource leak.
+
+ * The `context` Solution: You create a single context object at the beginning of the operation. You then pass this ctx to
+ every function and goroutine involved in the task. The original function holds a "cancel function." When the user
+ cancels, this function is called.
+ * All the downstream functions and goroutines are written to periodically check if the context has been cancelled
+ (e.-g., select { case <-ctx.Done(): return }).
+ * As soon as they see the cancellation signal, they stop their work, clean up any resources they were using, and exit
+ gracefully.
+
+ This allows for a clean, cascading shutdown of a whole tree of concurrent operations.
+
+ Example:
+
+ 1 func main() {
+ 2 // Create a context that can be cancelled.
+ 3 ctx, cancel := context.WithCancel(context.Background())
+ 4
+ 5 // Simulate cancelling the operation after 2 seconds.
+ 6 time.AfterFunc(2*time.Second, cancel)
+ 7
+ 8 // Start a long-running operation, passing it the context.
+ 9 longRunningOperation(ctx)
+ 10 }
+ 11
+ 12 func longRunningOperation(ctx context.Context) {
+ 13 for {
+ 14 select {
+ 15 case <-ctx.Done():
+ 16 // The context was cancelled! Clean up and exit.
+ 17 fmt.Println("Operation cancelled. Cleaning up.")
+ 18 return
+ 19 default:
+ 20 // Do a small piece of work.
+ 21 fmt.Println("Working...")
+ 22 time.Sleep(500 * time.Millisecond)
+ 23 }
+ 24 }
+ 25 }
+
+ 2. Timeouts and Deadlines
+
+ This is a specialized form of cancellation. It's extremely common in network clients and servers.
+
+ * The Problem: You make a request to a database or another microservice. What if that service is slow or completely
+ unresponsive? Your request could hang forever, holding up a connection and a goroutine.
+
+ * The `context` Solution: You create a context with a timeout.
+
+ 1 // This context will be automatically cancelled after 3 seconds.
+ 2 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
+ 3 defer cancel() // Good practice to always call cancel
+ 4
+ 5 // Now, make the database call with this context.
+ 6 db.QueryRowContext(ctx, "SELECT ...")
+ The database/sql driver (and any well-written modern Go library) is designed to accept a ctx. It will monitor the
+ context and, if the 3-second timeout is reached before the query finishes, it will automatically cancel the database
+ query on the server and return a timeout error.
+
+ 3. Request-Scoped Values
+
+ This is the third, and least common, use case. A context can carry a lightweight key-value store.
+
+ * The Problem: You have a web request, and you need to pass a request ID or a user's authentication token through many
+ layers of your application (from the HTTP handler to the middleware to the business logic to the database layer).
+ Passing these values as explicit arguments to every single function would be cumbersome and clutter the function
+ signatures.
+
+ * The `context` Solution: You can store these values in the context at the beginning of the request.
+
+ 1 // In your middleware or HTTP handler:
+ 2 ctx := context.WithValue(r.Context(), "requestID", "xyz-123")
+ 3 // Now call the next handler with the new context.
+ 4 businessLogic(ctx, ...)
+ 5
+ 6 // In your businessLogic function:
+ 7 func businessLogic(ctx context.Context, ...) {
+ 8 // Retrieve the value later.
+ 9 if reqID, ok := ctx.Value("requestID").(string); ok {
+ 10 log.Printf("Handling request %s", reqID)
+ 11 }
+ 12 }
+ Important Caveat: This should only be used for request-scoped data that is optional or informational. It should not be
+ used for required parameters, as that makes your function's dependencies hidden and unclear.
+
+ The Rule of Thumb
+
+ If a function is doing I/O (network, disk), waiting for anything, or could potentially be slow, it should accept a
+ context.Context as its first argument. This has become a strong convention in the Go community.
+
+ func DoSomething(ctx context.Context, arg1 string, arg2 int) error
+