惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy & Cybersecurity Law Blog
Engineering at Meta
Engineering at Meta
Forbes - Security
Forbes - Security
MongoDB | Blog
MongoDB | Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
A
About on SuperTechFans
量子位
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
雷峰网
雷峰网
腾讯CDC
P
Proofpoint News Feed
S
Schneier on Security
S
Secure Thoughts
V
Visual Studio Blog
Help Net Security
Help Net Security
The Hacker News
The Hacker News
C
Cyber Attacks, Cyber Crime and Cyber Security
P
Privacy International News Feed
SecWiki News
SecWiki News
S
SegmentFault 最新的问题
T
Threatpost
小众软件
小众软件
MyScale Blog
MyScale Blog
F
Fortinet All Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
P
Proofpoint News Feed
T
Tailwind CSS Blog
I
Intezer
C
CERT Recently Published Vulnerability Notes
U
Unit 42
V
V2EX
Cyberwarzone
Cyberwarzone
Recorded Future
Recorded Future
O
OpenAI News
Project Zero
Project Zero
有赞技术团队
有赞技术团队
Google DeepMind News
Google DeepMind News
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
Know Your Adversary
Know Your Adversary
C
Cybersecurity and Infrastructure Security Agency CISA
Scott Helme
Scott Helme
V2EX - 技术
V2EX - 技术
博客园 - 叶小钗
S
Securelist
A
Arctic Wolf
The Cloudflare Blog
W
WeLiveSecurity
T
Threat Research - Cisco Blogs
博客园 - Franky

Lobsters

Lunacy | Red Vice CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Package managers that package package managers Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? CSS vs. JavaScript • Josh W. Comeau Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Back to the Building Blocks’ Building Blocks Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data 5 PostgreSQL locking behaviors that trip people up okmij.org Stop advertising in your commits! | AksDev GitHub - mplsllc/macsurf: A modern web browser for Classic Mac OS 9 PowerPC. Real CSS3, ES5 JavaScript, native HTTPS — built with CodeWarrior on the Carbon API. Introducing DoomBench - Can Your Data Stack Run DOOM? What are some of your favourite developer tools? Building a Scalable Ingestion Pipeline with Temporal (Part 1) Converting shallow Git bundles into normal repositories Are you a member of any professional associations? What is a harmonic? An interactive comic about additive synthesis How Virtual Tables Work in the Itanium C++ ABI Using SwiftUI to Build a Mac-assed App in 2026 Rust (and Slint) on a jailbroken Kindle. ~jack/lambda-on-lambda - Serverless Haskell on AWS - sourcehut git Human proof for FOSS contributions Extremely simple internet radio controlled via IRC Announcing BABLR Splitting Konsole views from Helix to run tools | AksDev GitHub - yugr/rust-slides Serving files over HTTP three ways: synchronous, epoll, and io_uring update docs with information about building with build.py (#979) · astral-sh/python-build-standalone@c9c40c5 A Simple Makefile Tutorial On C extensions, portability, and alternative compilers Switching to Colemak | Pedro Alves Just How Bad Was The Intel IAPX432? Nix's Substituter List Is Not a Routing Table Accelerating copy_if using SIMD Lambda on Lambda: Serverless Haskell on AWS | Blog Announcing feed-repeat v1.0 Scaling Akvorado BMP RIB with sharding EYG news: A host of CLI improvements, new guides and new effects The social contract of writing JS Crossword C array types are weird; and related topics Flatpak will depend on systemd – OSnews Migrating from Go to Rust | corrode Rust Consulting A portentous reunion Vivado Licensing Options How my minimal, memory-safe Go rsync steers clear of vulnerabilities the entropy layer of a wavelet codec, on its own GitHub - nferhat/fht-compositor: A dynamic tiling Wayland compositor. Debian SE Linux and PinTheft Does bulk memmove speed up std::remove_if? (No.) 声明式部分更新 | Blog | Chrome for Developers Fully in-browser container builds Dianne Skoll's Web Site - Remind The Architecture of Open Source Applications (Volume 1)Berkeley DB Pardon MIE? - ironPeak Blog “Long-Term Support” doesn’t mean what you think Jira IS Turing-Complete May I recommend thinking of Emacs as your Fortress of Solitude hershey Floodgap Gopher-HTTP gateway gopher://thelambdalab.xyz/1cuneiforth/ HP QuickWeb, Singular And Pointless A new suite of modern tools coming for editing and publishing RFCs From the Tabletop… The Digital Antiquarian Building a Host-Tuned GCC to Make GCC Compile Faster Are we self-sovereign PKI yet? Claw Patrol: an open-source security firewall for agents | Deno Revised^7 Report on Scheme, Large: Procedural Fascicle Draft is now public A Network Allow-List Won't Stop Exfiltration — André Graf From AFSK to Goertzel – µArt.cz Software For My New Home Server Introducing Neptune: Direct3D virtualization for QEMU AI Agent Bankrupted Their Operator While Trying to Scan DN42 - Lan Tian @ Blog mimalloc: A new, high-performance, scalable memory allocator for the modern era Making wl_shm fast The Soul of Maintaining a New Machine - Third Draft | Books in Progress What is Git made of?
That one time I used Go panics for flow control
Posted on 2026-05-23. · 2026-05-23 · via Lobsters

How our protagonist discovered that a key service that powers our support was absurdly vulnerable to overload, and what we did to fix it.

Part of our support infrastructure at work is an in-memory datastore, that allows us to query our outstanding support work over various dimensions, such as work type, whether it's been put on hold for some reason, etc. It's functionally equivalent to a single table in an SQL database, where you have a single dataset, boolean filters and configurable sorting.

At work, we have an in-memory datastore that powers part of our support infrastructure. Its kind of analgous to having bitmap filters with post-hoc filtering, so any use of sort/limit will sort the entire result set. And the key part here, is that the result sets can be large enough that sorts can take one or two seconds.

And for a bit of context, this service deployment wasn't autoscaled at the time, and upstream services will retry failed requests. Sometimes after a relatively short timeout. Which is fun.

So, one day, this service had more query load than it can handle; and because of the inelasticity, it got overloaded, and queries started to take way longer (like, up to a minute vs. a typical time of up to 1-2s). Unfortunately, because this was an incident, and sometimes the panic sets in, one of my theories was that memory had gotten slower. Which of course was absurd, but under time presssure, incident brain can be very real.

However, as earlier foreshadowed, this service had simply became overloaded, so we not only had slightly higher than average demand, but also failure demand from retries. Most of the time in a Go service, we pass around a context, so that when the caller gives up on us, we can cancel the operation, short-circuit and bail early.

However, when we were able to get a cpu profile and take a look, the vast majority of the CPU time was taken up in the sort phase of the query. In go, none of the sort functions support cancellation (reasonably so, as normally you're either in a batch context, or sorting small enough counts that the time taken isn't significant). So, what to do?

Normally, context cancellation has leaf functions check for an error, and then propagate it via the typical errors-as-values mechanism. However, none of the sort functions (eg: sort.Sortfunc) take a context, or allow returning an error.

Thankfully, Go has another, non-local signalling mechanism for handling errors (eg: if you've dereferenced a nil pointer), in the form of panics. This tends not to be used much for error handling per-se, because the non-local flow control can be harded to reason about, but it can make sense within a single narrowly defined context.

For example, the encoding/json package does this, for example throwing via json.(*encodingState).error(…), and recovering within the scope of the top level json.(*encodingState).marshal(…) function. So no client code actually sees the non-local control flow, and no engineers experience unexpected panics.

So we changed the code from something like this:

func execute(ctx context.Context) (results, error) {
    resultSet := query.filter(someTable)

    slices.SortFunc(resultSet, func(a, b Row) int {
        return query.compare(a, b)
    })
}

To something like this:

type nonLocalCancellation struct {err error}

func execute(ctx context.Context) (results, error) {
    // setup happens here
    resultSet := query.filter(someTable)

    var sortErr error
    defer func() {
        // Ref: https://go.dev/blog/defer-panic-and-recover
        if r := recover(); r != nil {
            if c, ok := r.(nonLocalCancellation); ok {
                sortErr = c.err
            } else {
                panic(r)
            }
        }
    }()

    slices.SortFunc(resultSet, func(a, b Row) int {
        if ctx.Err() != nil {
            panic(nonLocalCancellation{err})
        }
        return query.compare(a, b)
    })

    if sortErr != nil {
        return nil, sortErr
    }

    return resultSet, nil
}

Which, is a lot of messing about (it's an ugly solution to an ugly problem), but does mean if the caller gives up on the query, we don't waste time sorting a result for someone who will never care about it.