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

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
J
Java Code Geeks
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
量子位
T
Tailwind CSS Blog
Vercel News
Vercel News
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
U
Unit 42
Engineering at Meta
Engineering at Meta
L
LangChain Blog
大猫的无限游戏
大猫的无限游戏
D
Docker
博客园_首页
P
Proofpoint News Feed
月光博客
月光博客
T
The Blog of Author Tim Ferriss
MyScale Blog
MyScale Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Martin Fowler
Martin Fowler
腾讯CDC
N
Netflix TechBlog - Medium
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

Buttondown's blog

Email could have been X.400 times better The physicists who convinced Fermilab to send Brazil's emails Better in-app previews Analytics 3.0 Subscriber ID variables Comments! Send latest premium action Automation filtering Free API subscribers Surveys in automations Reply to replies Labels for RSS feeds How Jeremy Singer-Vine curates curious datasets for readers 2023 (and what's next) Email vs web content Sort by engagement Better gift subscriptions How Andy Dehnart built a career reviewing television New email template Email-based automations Opt-in reply tracking Automatic alt text More social network integrations Sort by metadata Overlarge image warnings Automation tag actions Pause emails mid-flight Search tags and automations Gift via automations Subscriber-driving emails
Public postmortem: bot identification traffic blocking
Justin Duke · 2025-07-17 · via Buttondown's blog

Our public postmortem for Incident #0011.

Justin Duke

Justin Duke

July 17, 2025

TL;DR

For the past few days, we’ve been experiencing thundering herd-esque downtime every time we deploy at the top of the hour. The top of the hour nuance is not actually that uncommon: we serve a lot of RSS traffic. (A fun fact is that 80% of our page views come from RSS readers and scrapers and the vast majority of them are fairly naive and run the equivalent of an hourly cron to ping every single RSS feed in which they are interested.)

After investigation, we were able to identify the reason why this started happening recently, even though the above traffic pattern and our general CI/CD posture has remained unchanged. Part of our firewall system involves checking incoming IP addresses against a deny list culled from a variety of trusted sources. In order to make the firewall as performant as possible, we aggressively cache that list so that we're not pulling it every time a subscription attempt is made. However, we recently changed the logic to expand the purview of the database that held those IP addresses to also store aggregate-level data about IPs for telemetry purposes. At a high level, the logic looked something like this:

@cache
def get_problematic_ip_addresses():
    ip_address_models = IPAddress.objects.all()
    return {
        ip.ip_address
        for ip in ip_address_models
        if ip.do_not_honor
    }

And that logic remained the same! But that backing IPAddress model went from a few hundred records to a few hundred thousand, replete with a JSON payload for each IP. And because we were caching this, it meant that even with rolling deploys, every single time a new server would come online, it would be aggressively unresponsive as it tried to pull and then collate every single IP address within the 30-second time span of a request. We’ve fixed this trivially:

@cache
def get_problematic_ip_addresses():
    ip_address_models = IPAddress.objects.filter(do_not_honor=True)
    return {
        ip.ip_address
        for ip in ip_address_models
    }

Going forward, we’ll be paying much closer attention to the actual timeline of the deploy process. It was easy to chalk this up to luck of the draw to a certain extent, but such “luck” is scarce and still came at the cost of severely degraded performance. By logging and alerting on startup time and deviations thereof, we’ll be able to more actively identify aberrations of this nature in the future.