Go 1.8 makes service latency easier to control
Shorter garbage collection pauses accompany explicit HTTP shutdown and wider context support. The changes address runtime delays and the lifetime of application work.
A service can finish its handler quickly and still delay a request because the runtime has stopped execution. It can also accept work quickly and then abandon that work when the process exits. Go 1.8 addresses both problems, though through different mechanisms. The collector removes stop-the-world stack rescanning, while the HTTP server gains an explicit graceful shutdown operation. Cancellation reaches further into the standard library, connecting request lifetimes to operations that may otherwise outlast them.
The February 16 announcement reports garbage collection pauses usually below 100 microseconds, with some as low as 10 microseconds. Those are statements about the implementation and its observed behavior, not a deadline guarantee for every service. The release notes identify the specific removed operation and describe the new library contracts. Together they explain why this release matters beyond a collection of benchmark improvements.
I value Go because its ordinary code remains readable without a large amount of private framework knowledge. Explicit shutdown and cancellation fit that preference. They make decisions about the lifetime of work visible in standard library calls, where another reader can find them.
Removing a collector pause
The release notes attribute the shorter collector pauses to eliminating stop-the-world stack rescanning. This is a more useful description than saying that garbage collection has become faster. It identifies work that previously required stopping application execution. Removing that work from the stopped interval changes how the collector interrupts a running program, even when the application has not changed its allocation behavior.
Pause duration and total execution time answer different questions. A lower pause can help a request that happens to overlap collection, while compiler improvements can reduce the CPU time spent executing the request itself. Go 1.8 contains both kinds of improvement. Treating them as one speed figure would conceal which part of service behavior each change affects.
The compiler backend introduced for 64-bit x86 in Go 1.7 now runs on every architecture. It uses static single assignment form, which the release notes describe as a better basis for optimizations such as bounds check elimination. The project reports a reduction of 20 to 30 percent in CPU time for its benchmark programs on 32-bit ARM. The corresponding improvement on 64-bit x86 is a more modest zero to ten percent because that architecture already used the backend.
Those figures belong to the projects benchmark programs and their architectures. They do not establish a universal application speedup. They do establish an important change in implementation coverage. Optimizations developed in the new backend now apply across the supported architectures, and the temporary compiler flag that disabled it has disappeared.
Compilation itself also improves. The announcement puts the gain over Go 1.7 at about 15 percent, while acknowledging that further work remains. Faster builds reduce the cost of obtaining feedback, but they are distinct from the generated programs performance. A release can improve both without requiring either figure to stand in for the other.
Giving an HTTP server time to finish
Graceful shutdown is an API for coordinating a process exit with active work. Before writing lifecycle code around it, the exact ordering matters. The Go 1.8 server implementation documents Server.Shutdown as closing listeners first, closing idle connections, and waiting for active connections to return to idle. It then closes those connections too.
Closing the listeners prevents the server from accepting additional connections while it drains existing work. Closing idle connections prevents unused keep-alive connections from extending the drain. Active connections retain an opportunity to finish. These operations serve different purposes, so replacing all of them with an immediate close changes the behavior seen by clients whose requests are already underway.
The context passed to Shutdown limits the waiting period. If it expires first, the method returns the context error. The source does not describe expiry as an instruction to terminate all remaining application work. A shutdown deadline therefore needs an explicit policy for what the caller does after that error. The new Server.Close method provides abrupt closure of ordinary active connections when that is the intended next action.
A small helper can keep those two choices visible. Here srv is the server being stopped, and the caller remains responsible for keeping the process alive until this function returns.
func stopServer(srv *http.Server) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
srv.Close()
return err
}
return nil
}
The ten seconds are an example policy, not a Go default. The helper attempts a bounded drain and then closes remaining ordinary connections if draining fails. It preserves the shutdown error so the caller can distinguish a completed drain from the fallback. A real process also has to coordinate its serving goroutine and exit path, because returning from the main function would end the opportunity to wait.
Hijacked connections require separate coordination. The versioned documentation specifically excludes connections such as WebSockets from both the waiting performed by Shutdown and the connections known to Close. Once application code has taken ownership of such a connection, it also owns the notification and completion protocol for stopping it. The standard server cannot infer when an application-level conversation has finished.
I prefer this boundary to a shutdown method that appears to promise more than it can observe. Ordinary HTTP requests have a lifecycle the server understands. A hijacked connection has application semantics that the server no longer controls, and the API documents that distinction.
Propagating the request lifetime
Graceful process termination only handles one reason work should stop. A request may also become irrelevant because its client disconnects or its allowed time expires. Go 1.8 extends the use of context.Context introduced into the standard library in Go 1.7. The release notes connect that extension to database operations and name resolution as well as server shutdown.
The HTTP server now cancels the context returned by Request.Context when the underlying network connection closes. A handler can pass that context to operations below it, allowing those operations to observe that the requesting client has gone away. This depends on the underlying net.Conn obeying its documented semantics, a requirement the release notes call out for custom connection implementations.
In database/sql, new methods such as QueryContext and PrepareContext take contexts explicitly. The package can cancel a wait for an available pooled connection, and the notes explain that responsive cancellation of an in-progress query requires driver support. Merely changing a method name in application code cannot make an older driver interrupt work it does not know how to cancel.
That distinction matters under pressure. Waiting for a connection and executing a query are separate phases with different owners. The connection pool can respond to a canceled context while the operation is still waiting inside the pool. After the driver has started database work, its implementation determines whether cancellation reaches that work promptly. The same application-level context can span both phases without making their capabilities identical.
The new resolver APIs also accept contexts. This extends a common lifetime mechanism into another operation that can delay a request before useful application work begins. The practical advantage is that cancellation policy can travel through standard interfaces instead of requiring a separate timeout convention for every subsystem.
Keeping common code explicit
The release also reduces a smaller source of routine ceremony. sort.Slice accepts a slice and a comparison function, so sorting a slice of structures no longer requires defining a separate sorter type solely to provide the interface methods. The announcement illustrates ordering structures by a name field. The comparison remains visible at the call site, which is where a reader needs to understand the ordering rule.
This is consistent with the releases larger library changes. The runtime takes on implementation work that applications should not have to manage, while the library exposes policy where the application must choose it. Collector stack rescanning belongs to the runtime. A shutdown deadline belongs to the program operating the server. Query cancellation requires cooperation between the caller, the pool, and the driver.
Go 1.8 also adds mutex contention profiling through the test command and runtime support. The notes identify a limitation in this release, where the profile reports contention for sync.Mutex but not sync.RWMutex. That gives service authors another concrete tool for examining delays left after compiler and collector improvements. It addresses time spent waiting for a lock, which shorter collection pauses and graceful shutdown do not themselves explain.