Vad en bot på över 2 000 Discord-servrar har lärt oss
Fyra saker som fungerar annorlunda i stor skala än man tror medan man skriver koden.

En Discord-bot på tre servrar och samma bot på över tvåtusen är inte samma program i större format. En handfull antaganden som är helt ofarliga i det lilla blir, efter en viss storlek, precis de buggar som är svårast att hitta.
Här är fyra som kostade oss verkliga avbrott.
1. Fel sprider sig inte jämnt, de gömmer sig
AlphaCloud körs uppdelad på flera processer, så kallade shards. Discord bestämmer vilken server som hamnar på vilken shard, och varje shard känner bara till sina egna servrar.
Det ger en klass av fel som utifrån ser ut som slump: välkomstmeddelanden fungerar på vissa servrar och inte på andra. Inget fel i loggen, inget larm, ingenting. Orsaken var ett enda nätverksanrop utan timeout. Om motparten accepterar anslutningen och sedan tystnar väntar koden resten av processens livstid. Allt efter det händer aldrig, och eftersom inget fel kastas rapporterar ingenting något fel.
Lärdomen är ospektakulär och universell: varje utgående anrop behöver en övre gräns. Ett anrop som väntar för evigt är värre än ett som misslyckas, för ett misslyckande syns åtminstone.
2. En omstart är ingen lösning, den är en tidsfrist
Medan vi inte kände till orsaken hjälpte det pålitligt att starta om den drabbade sharden. Det är precis problemet med omstarter: de fungerar, och det tar bort trycket att hitta orsaken.
Nu behandlar vi "hjälper en omstart?" som en diagnos, inte som ett botemedel. Om den gör det lever det trasiga tillståndet i minnet, och det finns ett ställe i koden som bygger upp det och aldrig river ner det.
Det omvända gäller också: allt som måste överleva en omstart hör hemma i databasen. Om en åtgärd i flera steg pågår, säg en rolländring för tusentals medlemmar, och processen dör halvvägs får åtgärden inte bara försvinna. Den måste upptäckas och fortsätta vid nästa start.
3. Discords gränser gäller per bot, inte per process
Discord begränsar hur många anrop en bot får göra per sekund. Det självklara antagandet: fler processer, mer genomströmning. Fel. Gränsen gäller boten, inte processen. Tre shards med var sin full budget blir tillsammans tre gånger tilldelningen, och Discord stryper då alla tre.
Därför räknar var och en av våra processer ut hur många det finns totalt och tar bara sin andel. Det låter självklart, men var det inte, och buggen förblev osynlig länge eftersom varje enskild process såg helt korrekt ut för sig själv.
En närliggande fälla: återkommande jobb körs med fasta intervall, säg var femte minut. Om alla shards startar samtidigt kör de sina jobb i samma ögonblick för all framtid. Sedan dess får varje process en slumpmässig förskjutning vid start.
4. Det folk faktiskt ställer in är inte det du väntar dig
Den tekniska delen är ena halvan. Den andra: för de flesta servrar är standardvärdet inställningen. En modul vars standardvärden är fel är felkonfigurerad på den absoluta majoriteten av servrar, hur bra inställningssidan än är.
Två saker följer av det för oss. För det första kräver varje standardvärde mer eftertanke än själva inställningen. För det andra är den bättre frågan för en ny modul inte "vad kan ställas in här" utan "vad gör den här modulen när ingen ställer in något, och är det rätt för en vanlig server".
Det är också skälet bakom ett beslut som först låter som motsatsen: sedan v1.8.0 kan nästan varje meddelande som boten skickar utformas fritt. Men obligatoriska block som anledningen i ett varnings-DM eller kategorimenyn i en ticketpanel kan flyttas men inte raderas. Frihet i utformningen, ja, men inte så långt att en server av misstag bygger något som ser ut att fungera och inte gör det.
Den röda tråden
Tre av de fyra är samma tanke i olika kostymer: i den här storleken är ett tyst fel dyrare än ett högljutt. En krasch rapporteras och åtgärdas. En funktion som helt enkelt slutar göra något kan förbli så i månader, och du får veta det genom en buggrapport från någon som tog sig besväret att skriva.
Så frågan vi nu ställer först om ny kod är inte "fungerar det här", utan "hur ser det ut när det inte gör det, och skulle vi märka det".
Fler artiklar
Koll på 4 000 Discord-servrar: Grafana, PostHog och vad de visade oss
En loop som i tysthet stod för halva vår trafik, tre miljoner meningslösa förfrågningar om dagen och en attack som var över efter en minut. Vad vår övervakning ser, och vad den medvetet inte ser.
Bakom kulissernaNär vår AI-leverantör pensionerade modellen
Ett modellbyte, tomma svar utan fel och en faktura som plötsligt blev flera gånger högre. Vad som gick sönder, och varför orsaken var så svår att se.
Bakom kulissernaLagen i vår kod
En uppsägningsknapp som måste fungera utan inloggning. Ett formulär som aldrig får avvisa dig. Varför rättsliga skyldigheter är arkitektur för oss, inte standardtext.