Language Internals
Part 11 of 11 · Go Language ProficiencyAdvanced Sync, Profiling & Production
sync primitives, pprof, the goroutineleak profile, and a production checklist.
- 1Gist
- 2Maps
- 3Q&A
- 4Sandbox
Voice readout needs Web Speech Synthesis in this browser.
Question ladder
L1
What guards an in-place map?
Answer
sync.Mutex around the write and the read. defer Unlock so a panic still releases it.
L2
Mutex or channel for a counter?
Answer
A mutex or an atomic. A channel is extra machinery for a value that never needs an owner handoff.
L3
What is sync.Once for?
Answer
Initialization that must run one time even if several goroutines arrive together.
L4
What must you do before Pool.Put?
Answer
Clear the buffer. The next Get must not observe the previous caller's bytes.
L5
Where may pprof listen?
Answer
Localhost, or behind authentication. A public /debug/pprof is a data leak and a CPU lever.
L6
What did Go 1.27 make generally available?
Answer
The goroutineleak profile, for goroutines that are permanently blocked.
L7
What else is on the ship checklist?
Answer
go test -race, HTTP timeouts, Shutdown, context on outbound calls, percent-w wrapping, a toolchain set (go 1.27 minimum + toolchain go1.27.1; pin the exact image in CI), little cgo, and a limit on fan-out.
Failure modes
Public pprof
Anyone who can reach the port can dump heap and goroutines.
Lock held across a call
The mutex stays locked while a handler waits on the network.
Pool buffer reused dirty
The next request reads the previous payload.
cgo on the hot path
The call crosses into C and the scheduler cost dominates a small function.
Misconceptions
A channel is the production default for every shared value.
Shared maps and counters want a mutex or an atomic. Channels hand off ownership.
pprof is safe on the public mux because it is a debug route.
Bind it to localhost or require auth.
GOMAXPROCS should always equal a huge number.
The default tracks the available CPUs. Raising it without a measurement just adds scheduler work.
The race detector replaces a profile.
The detector finds conflicting accesses in tests. Profiles explain a running process.
Interviewer traps
Protecting a counter with a goroutine and a channel.
Name atomic or Mutex and why a channel is the wrong size.
Importing net/http/pprof on the public server.
Say the listen address or the auth gate.
Calling Pool a cache.
It reuses scratch space. It does not remember your keys.
Design scenario
Same prompt for every reader.
Requirements
Mutex on the map, atomic or mutex on the counter, pprof on localhost, goroutineleak available, race tests clean.
Traffic / scale
Many handlers update the counter and read the cache.
Latency
The lock is not held during the downstream call.
Consistency
A failed fetch does not publish a partial cache entry.
Availability
A permanently blocked worker shows up in the leak profile.
Failure assumptions
- The cache map is written from handlers with no lock.
- pprof is registered on the public Server.
Constraints
- Stay on sync and the diagnostics tools.
- Do not add a tracing vendor.
Prompt
A cache map and a request counter ship in one process, with profiles available to on-call and not to the internet.
A lock next to the data
Prefer
Mutex or atomic
The critical section is visible and short. Profiles explain the process you shipped.
- defer Unlock.
- pprof stays private.
- The leak profile is for stuck goroutines.
Alternative
A channel around a counter
The extra goroutine serializes a value an atomic already updates.
- More blocking.
- A closer to get wrong.
- No clearer profile.
Overview
sync.Mutex protects a map or struct in place. sync.Once runs init once. sync.Pool recycles buffers that you clear first. Profiles tell you whether you are burning CPU, heap, or goroutines. Go 1.27.1 includes the goroutineleak profile as a general tool.
Decisions
- 1
Prod issue
- nextCPU or memory?
- ?
CPU or memory?
- CPUCPU profile
- memoryHeap profile
- 3
CPU profile
- 4
Heap profile
- nextStuck or a race?
- ?
Stuck or a race?
- stuckGoroutine leak profile
- raceRace detector
- 6
Goroutine leak profile
- 7
Race detector
Lesson map
Advanced Sync, Profiling & Production
sync primitives, pprof, the goroutineleak profile, and a production checklist.
Architecture. Prod issue Ready. CPU or memory? Ready. CPU profile Ready. Heap profile Ready. Stuck or a race? Ready. Goroutine leak profile Ready. Race detector Ready
Select a node to see why it exists, or an edge to see the protocol, direction, effect, and consequence.
Mermaid export
flowchart TB Issue["Prod issue Ready"] Kind["CPU or memory? Ready"] Cpu["CPU profile Ready"] Heap["Heap profile Ready"] Stuck["Stuck or a race? Ready"] Leak["Goroutine leak profile Ready"] Race["Race detector Ready"] Issue -->|continues| Kind Kind -->|CPU| Cpu Kind -->|memory| Heap Heap -->|continues| Stuck Stuck -->|stuck| Leak Stuck -->|race| Race
Press Run. Snippets must be self-contained — no network, files, or native modules.
The Python lock would wrap set if threads shared the dict. The Go mutex is that same bracket around the map.
Rosetta — mutex
package cache
import "sync"
type Cache struct {
mu sync.Mutex
m map[string]string
}
func (c *Cache) Set(k, v string) {
c.mu.Lock()
defer c.mu.Unlock()
if c.m == nil {
c.m = map[string]string{}
}
c.m[k] = v
}const m = new Map<string, string>();
function set(k: string, v: string) {
m.set(k, v);
}import threading
class Cache:
def __init__(self) -> None:
self._mu = threading.Lock()
self._m: dict[str, str] = {}
def set(self, k: str, v: str) -> None:
with self._mu:
self._m[k] = vHold the mutex only around the map. Do not hold it across a network call. A TypeScript Map on one event-loop thread does not need this lock. Worker threads and Python threads do. sync.Once covers one-time setup. sync.Pool is for buffers: clear them before Put.
Rosetta — pprof
package main
import (
"log"
"net/http"
_ "net/http/pprof"
)
func main() {
go func() {
log.Println(http.ListenAndServe("127.0.0.1:6060", nil))
}()
select {}
}
// go tool pprof http://127.0.0.1:6060/debug/pprof/heap// node --inspect app.js
// Chrome DevTools, clinic, or 0x for a CPU view
const marker = "inspect";
console.log(marker);# py-spy, cProfile, or scalene against a pid
marker = "profile"
print(marker)The blank import registers pprof on the default mux. Binding 127.0.0.1 keeps it off other interfaces. Behind auth is the other acceptable gate. The Go 1.27 goroutineleak profile reports goroutines that are permanently blocked. Heap and CPU profiles still answer the other two questions. go test -race is the test-time partner, not a substitute.
Production checklist
go test -raceis clean on packages that share memory.- HTTP server timeouts are set, and shutdown uses
Server.Shutdown. - Outbound calls take a context.
- Errors wrap with
%w. - pprof is not public without auth.
- Toolchain set (go 1.27 minimum + toolchain go1.27.1; pin the exact image in CI).
- cgo stays off the hot path unless a measurement says otherwise.
- Fan-out has a limit.
GOMAXPROCS defaults to the usable CPU count. Change it when a profile says the scheduler is the problem, not because a blog used a round number.
Interview Q&A
Mutex or channel for a counter?
Answer
Mutex or atomic. A channel is the wrong size unless you are handing off ownership of a larger value.
Why defer Unlock?
Answer
A panic in the critical section still releases the mutex. Forgetting the unlock deadlocks the next caller.
Where do you bind pprof?
Answer
Localhost, or an authenticated route. Never a public bare debug port.
What is goroutineleak in Go 1.27?
Answer
A profile that finds goroutines blocked with no way to proceed. It is generally available in this release.
What do you clear on a Pool?
Answer
The buffer contents, before Put. The next Get must not see leftover bytes.
Does the race detector run in production?
Answer
It is a test build. Production uses profiles. Ship the race-clean tests, then profile the process.
When is cgo worth it?
Answer
When a measurement shows the C library is the only way to hit the number, and you accept the build and call cost.
Which pin belongs in go.mod?
Answer
Toolchain set (go 1.27 minimum + toolchain go1.27.1; pin the exact image in CI), so the leak profile and the compiler match this series.
Pitfalls
- Locking, then calling the network.
- Registering pprof on the public server.
- A Pool used as a user cache.
- Raising GOMAXPROCS with no profile.
A handler never returns. Say which profile you open first, which address is allowed to serve it, and which test flag you already required in CI.