Contexts, cancellations, and bounded work—plus Chapter 8 from Mastering Go#14Mihalis Tsoukalos on Go’s Concurrency DisciplineContexts, cancellations, and bounded work—plus Chapter 8 from Mastering GoMastering Memory in C++ with Patrice RoyMore than 70% of severe vulnerabilities come from memory safety errors. This masterclass will show you how to write C++ that isn’t part of that problem.Join Patrice Roy — ISO C++ Standards Committee member and author of C++ Memory Management — for a 2-day live masterclass on writing safe, efficient, and robust C++ code.What you’ll learn (hands-on):✔Smart pointers and RAII for predictable ownership✔Exception-safe, high-performance techniques✔Debugging leaks, alignment, and ownership issues✔Building memory-safe code that performs under pressurePatrice has taught C++ since 1998, trained professional programmers for over 20 years, and speaks regularly at CppCon. This masterclass distills his experience into practical skills you can apply immediately in production.Use code DEEPENG30 for 30% off.Register NowHi Welcome to the fourteenth issue of Deep Engineering.Go 1.25 has arrived with container-aware GOMAXPROCS defaults—automatically sizing parallelism to a container’s CPU limit and adjusting as limits change—so services avoid kernel throttling and the tail-latency spikes that follow. This issue applies the same premise at the code level—structure concurrency to real capacity with request-scoped contexts, explicit deadlines, and bounded worker pools—so behavior under load is predictable and observable.For today’s issue we spoke with Mihalis Tsoukalos, a UNIX systems engineer and prolific author of Go Systems Programming and Mastering Go (4th ed.). He holds a BSc (University of Patras) and an MSc (UCL), has written for Linux Journal, USENIX ;login:, and C/C++ Users Journal, and brings deep systems, time-series, and database expertise.We open with a feature on request-scoped concurrency, cancellations, and explicit limits—then move straight into the complete Chapter 8: Go Concurrency from Mastering Go. You can watch the interview and read the complete transcript here, or scroll down for today’s feature.📢 Important: Deep Engineering is Moving to SubstackIn two weeks, we’ll be shifting Deep Engineering fully to Substack. From that point forward, all issues will come from [email protected] ensure uninterrupted delivery, please whitelist this address in your mail client. No other action is required.You’ll continue receiving the newsletter on the same weekly cadence, and on Substack you’ll also gain more granular control over preferences if you wish to adjust them later.We’ll send a reminder in next week’s issue as the cutover approaches.Sign Up |AdvertiseStructured Concurrency in Go for Real-World Reliability with Mihalis TsoukalosGo’s structured concurrency model represents a set of disciplined practices for building robust systems. By tying goroutines to request scopes with context, deadlines, and limits, engineers can prevent leaks and overload, achieving more predictable, observable behavior under production load.Why Structured Concurrency Matters in Go (and What It Prevents)In production Go services, concurrency must be deliberate. Structured concurrency means organizing goroutines with clear lifecycles—so no worker is left running once its purpose is served. This prevents common failure modes like memory leaks, blocked routines, and resource exhaustion from runaway goroutines.As Mihalis Tsoukalos emphasizes, concurrency in Go “is not just a feature—it’s a design principle. It influences how your software scales, how efficiently it uses resources, and how it behaves under pressure”.Unstructured use of goroutines (e.g. spawning on every request without coordination) can lead to unpredictable latencies and crashes. In contrast, a structured approach ensures that when a client drops a request or a deadline passes, all related goroutines cancel promptly. The result is a system that degrades gracefully instead of accumulating ghosts and locked resources.Request-Scoped Concurrency with Context and CancellationGo’s context.Context is the cornerstone of request-scoped concurrency. Every inbound request or task should carry a Context that child goroutines inherit, allowing coordinated cancellation and timeouts. By convention, functions accept a ctx parameter and propagate it downward.As Tsoukalos advises, “always be explicit about goroutine ownership and lifecycle” by using contexts for cancellation—this way, goroutines “don’t hang around longer than they should, avoiding memory leaks and unpredictable behavior”.A common pattern is to spawn multiple sub-tasks and cancel all of them if one fails or the client disconnects. The golang.org/x/sync/errgroup package provides a convenient way to manage such groups of goroutines with a shared context. Using errgroup.WithContext, each goroutine returns an error, and the first failure cancels the group’s context, immediately signaling siblings to stop. Even without this package, you can achieve similar structure with sync.WaitGroup and manual cancellation signals, but errgroup streamlines error propagation.The following is a snippet from Mastering Go, 4th Ed. demonstrating context cancellation in action. A goroutine is launched to simulate some work and then cancel the context, while the main logic uses a select to either handle normal results or react to cancellation:c1, cancel := context.WithCancel(context.Background())defer cancel()go func() { time.Sleep(4 * time.Second) cancel()}()select {case <-c1.Done(): fmt.Println("Done:", c1.Err()) returncase r := <-time.After(3 * time.Second): fmt.Println("result:", r)}Listing: Using context.WithCancel to tie a goroutine’s work to a cancelable context.In this example, if the work doesn’t finish before the context is canceled (or a 3-second timeout elapses), the Done channel is closed and the function prints the error (e.g. “context canceled”). In real services, you would derive the context from an incoming request (HTTP, RPC, etc.), use context.WithTimeout or WithDeadline to bound its lifetime, and pass it into every database call or external API request. All goroutines spawned to handle that request listen for ctx.Done() and exit when cancellation or deadline occurs. This structured approach prevents goroutine leaks – every launched goroutine is tied to a request context that will be canceled on completion or error. It also centralizes error handling: the context’s error (such as context.DeadlineExceeded) signals a timeout, which can be logged or reported upstream in a consistent way.Bounding Concurrency and Backpressure with Semaphores and ChannelsAnother key to structured concurrency is bounded work. Go’s goroutines are cheap, but they aren’t free – unchecked concurrency can exhaust memory or overwhelm databases.Tsoukalos warns that just because goroutines are lightweight, you shouldn’t “spin up thousands of them without thinking. If you’re processing a large number of tasks or I/O operations, use worker pools, semaphores, or bounded channels to keep things under control”.In practice, this means limiting the number of concurrent goroutines doing work for a given subsystem. By applying backpressure (through limited buffer channels or tokens), you avoid queueing infinite work and crashing under load.One simple pattern is a worker pool: maintain a fixed pool of goroutines that pull tasks from a channel.This provides controlled concurrency — “you’re not overloading the system with thousands of goroutines, and you stay within limits like memory, file descriptors, or database connections,” as Tsoukalos notes.The system’s behavior under load becomes predictable because you’ve put an upper bound on parallel work.Another powerful primitive is a weighted semaphore. The Go team provides golang.org/x/sync/semaphore for this purpose. You can create a semaphore with weight equal to the maximum number of workers, then acquire a weight of 1 for each job. If all weights are in use, further acquisitions block – naturally throttling the input. The following code (from the Mastering Go chapter) illustrates a semaphore guarding a section of code that launches goroutines:Workers := 4sem := semaphore.NewWeighted(int64(Workers))results := make([]int, nJobs)ctx := context.TODO()for i := range results { if err := sem.Acquire(ctx, 1); err != nil { fmt.Println("Cannot acquire semaphore:", err) break } go func(i int) { defer sem.Release(1) results[i] = worker(i) // do work and store result }(i)}// Block until all workers have released their permits:_ = sem.Acquire(ctx, int64(Workers))Listing: Bounded parallelism with a semaphore limits workers to Workers at a time.In this pattern, no more than 4 goroutines will be active at once because any additional Acquire(1) calls must wait until a permit is released. The final Acquire of all permits is a clever way to wait for all workers to finish (it blocks until it can acquire Workers permits, i.e. until all have been released). Bounded channels can achieve a similar effect: for example, a buffered channel of size N can act as a throttle by blocking sends when N tasks are in flight. Pipelines, a series of stages connected by channels, also inherently provide backpressure – if a downstream stage is slow or a channel is full, upstream goroutines will pause on send, preventing unlimited buildup. The goal in all cases is the same: limit concurrency to what your system resources can handle. Recent runtime changes in Go 1.25 even adjust GOMAXPROCS automatically to the container’s CPU quota, preventing the scheduler from running too many threads on limited CPUgo.dev. By design, structured concurrency forces us to think in terms of these limits, so that a surge of traffic translates to graceful degradation (e.g. queued requests or slower processing) rather than a self-inflicted denial of service.Observability and Graceful Shutdown in PracticeStructured concurrency not only makes systems more reliable during normal operation, but also improves their observability and shutdown behavior. With context-based cancellation, timeouts and cancellations surface explicitly as errors that can be logged and counted, rather than lurking silently. For instance, if a database call times out, Go returns a context.DeadlineExceeded error that you can handle – perhaps logging a warning with the operation name and duration.These error signals let you differentiate between a real failure (bug or unavailable service) and an expected timeout. In metrics, you might track the rate of context cancellations or deadlines exceeded to detect slowness in dependencies. Similarly, because every goroutine is tied to a context, you can instrument how many goroutines are active per request or service. Go’s pprof and runtime metrics make it easy to measure goroutine count; if it keeps rising over time, that’s a red flag for leaks or blocked goroutines. By structuring concurrency, any unexpected goroutine buildup is easier to trace to a particular code path, since goroutines aren’t spawned ad-hoc without accountability.Shutdown sequences also benefit. In a well-structured Go program, a SIGINT (Ctrl+C) or termination signal can trigger a cancellation of a root context, which cascades to cancel all in-flight work. Each goroutine will observe ctx.Done() and exit, typically logging a final message. Using deadlines on background work ensures that even stuck operations won’t delay shutdown indefinitely – they’ll timeout and return. The result is a clean teardown: no hanging goroutines or resource leaks after the program exits.As Tsoukalos puts it, “goroutine supervision is critical. You need to track what your goroutines are doing, make sure they shut down cleanly, and prevent them from sitting idle in the background”.This discipline means actively monitoring and controlling goroutines’ lifecycle in code and via observability tools.Production Go teams often implement heartbeat logs or metrics for long-lived goroutines to confirm they are healthy, and use context to ensure any that get stuck can be cancelled. In distributed tracing systems, contexts carry trace IDs and cancellation signals across service boundaries, so a canceled request’s trace clearly shows which operations were aborted. All of this contributes to a system where concurrency is not a source of mystery bugs – instead, cancellations, timeouts, and errors become first-class, visible events that operators can understand and act upon.7-Point Structured Concurrency Checklist for ProductionContext Everywhere: Pass a context.Context to every goroutine and function handling a request. Derive timeouts or deadlines to avoid infinite waits.Always Cancel (Cleanup): Use defer cancel() after context.WithTimeout/Cancel so resources are freed promptly. Never leave a context dangling.Bound the Goroutines: Limit concurrency with worker pools, semaphores, or bounded channels – don’t spawn unbounded goroutines on unbounded work.Propagate Failures: Use errgroup or sync.WaitGroup + channels to wait for goroutines and propagate errors. If one task fails, cancel the rest to fail fast.Graceful Shutdown Hooks: On service shutdown, signal cancellation (e.g. cancel a root context or close a quit channel) and wait for goroutines to finish or timeout.Avoid Blocking Pitfalls: Use buffered channels for high-volume pipelines and select with a default or timeout case in critical loops to prevent global stalls.Instrument & Observe: Monitor goroutine counts, queue lengths, and context errors in logs/traces. A spike in “context canceled” or steadily rising goroutines means your concurrency is getting out of control.In Go, by consciously scoping and bounding every goroutine – and embracing cancellation as a normal outcome – engineers can build services that stay robust and transparent under stress. The effort to impose this structure pays off with systems that fail gracefully instead of unpredictably, proving that well-managed concurrency is a prerequisite for reliable production Go.🧠Expert InsightThe complete “Chapter 8: Go Concurrency” from Mastering Go, 4th ed. by Mihalis TsoukalosIn this comprehensive chapter, Tsoukalos walks you through the production primitives you’ll actually use: goroutines owned by a Context, channels when appropriate (and when to prefer mutex/atomics), pipelines and fan-in/out, WaitGroup discipline, and a semaphore-backed pool that keeps concurrency explicitly bounded.The key component of the Go concurrency model is the goroutine, which is theminimum executable entityin Go. To create a new goroutine, we must use thegokeyword followed by a function call or an anonymous function—the two methods are equivalent. For a goroutine or a function to terminate the entire Go application, it should callos.Exit()instead ofreturn. However, most of the time, we exit a goroutine or a function usingreturn because...Read the Complete Chapter🛠️ Tool of the WeekRay – Open-Source, High-Performance Distributed Computing FrameworkRay is an open-source distributed execution engine that enables developers to scale applications from a single machine to a cluster with minimal code changes.Highlights:Easy Parallelization: Ray offers a simple API (e.g. the @ray.remote decorator) to turn ordinary functions into distributed tasks, running across cores or nodes with minimal code modifications and hiding the complexity of threads or networking behind the scenes.Scalable & Heterogeneous: It supports fine-grained and coarse-grained parallelism, efficiently executing many concurrent tasks on a cluster.Resilient Execution: Built-in fault tolerance means Ray automatically retries failed tasks and can persist state (checkpointing), so even long-running jobs recover from node failures without manual intervention.Battle-Tested at Scale: It’s been deployed on clusters with thousands of nodes (over 1 million CPU cores) for demanding applications – demonstrating robust operation at extreme scale.Learn more about Ray📎Tech BriefsGo 1.25 is released: The version update brings improvements across tools, runtime, compiler, linker, and the standard library, along with opt-in experimental features like a new garbage collector and an updated encoding/json/v2 package.Container-aware GOMAXPROCS: Go 1.25 introduces container-aware defaults for GOMAXPROCS, automatically aligning parallelism with container CPU limits to reduce throttling, improve tail latency, and make Go more production-ready out of the box.Combine Or-Channel Patterns Like a Go Expert: Advanced Go Concurrency by Archit Agarwal: Explains the “or-channel” concurrency pattern in Go, showing how to combine multiple done channels into one so that execution continues as soon as any goroutine finishes, and demonstrates a recursive implementation that scales elegantly to handle any number of channels.Concurrency | Learn Go with tests by Chris James: Shows you how to speed up a slow URL-checking function in Go by introducing concurrency: using goroutines to check multiple websites in parallel, and channels to safely coordinate results without race conditions, making the function around 100× faster while preserving correctness through tests and benchmarks.Singleflight in Go : A Clean Solution to Cache Stampede by Dilan Dashintha: Explains how Go’s singleflight package addresses the cache stampede problem by ensuring that only one request for a given key is in-flight at any time, while other concurrent requests wait and reuse the result.That’s all for today. Thank you for reading this issue ofDeep Engineering. We’re just getting started, and your feedback will help shape what comes next. Do take a moment tofill out this short surveywe run monthly—as a thank-you, we’ll addone Packt creditto your account, redeemable for any book of your choice.We’ll be back next week with more expert-led content.Stay awesome,Divya Anne SelvarajEditor-in-Chief,Deep EngineeringIf your company is interested in reaching an audience of developers, software engineers, and tech decision makers, you may want toadvertise with us.*{box-sizing:border-box}body{margin:0;padding:0}a[x-apple-data-detectors]{color:inherit!important;text-decoration:inherit!important}#MessageViewBody a{color:inherit;text-decoration:none}p{line-height:inherit}.desktop_hide,.desktop_hide table{mso-hide:all;display:none;max-height:0;overflow:hidden}.image_block img+div{display:none}sub,sup{font-size:75%;line-height:0}#converted-body .list_block ol,#converted-body .list_block ul,.body [class~=x_list_block] ol,.body [class~=x_list_block] ul,u+.body .list_block ol,u+.body .list_block ul{padding-left:20px} @media (max-width: 100%;display:block}.mobile_hide{min-height:0;max-height:0;max-width: 100%;overflow:hidden;font-size:0}.desktop_hide,.desktop_hide table{display:table!important;max-height:none!important}}
Read more