Cases de Insucesso
investigando outages, bugs e outras falhas
roberta arcoverde
@rla4
rla4.com
/whois
- recifense
- programadora .NET desde 2005
- tech lead do stack overflow for teams
- co-host do hipsters.tech
- microsoft mvp
- @rla4
- gosta de minúsculas
por que falar sobre insucessos
dnad '15 - https://www.youtube.com/watch?v=qP4Jb9UBLsQ
o que esperar dessa palestra
cases interessantes de outages com diferentes razões (e diferentes soluções)
como monitoramos, identificamos e resolvemos problemas
links pra ferramentas de monitoramento e métricas open source
alívio por saber que você não é a única que já derrubou um site em produção
- desde 2008
- 20 devs no core team
- 10 no Stack Overflow for Teams
- 54+ milhões de usuários únicos/mês
tech stack
- .net core 3.1
- c#
- asp.net core
- sql server
- dapper, ef core
- typescript
- design system open source: stackoverflow.design
- redis
- elasticsearch
- ha proxy
9 WEB SERVERS
2 SQL SERVERS
LIVE
HOT STANDBY
Stack Overflow
528 M queries/dia
~5% CPU
ferramentas úteis: OpServer
https://opserver.github.io/Opserver/
ferramentas úteis: metrics
https://github.com/StackExchange/StackExchange.Metrics
- cliente .NET para reportar métricas
- handlers para Bosun, SignalFx, statsd, etc
- algumas métricas suportadas:
- estatísticas de GC
- gen0, gen1, gen2
- asp.net core
- req/s
- failed requests
- total requests
- process
- cpu
- thread count
- memória
- estatísticas de GC
falha #1: "escalando" migrations
falha #1: "escalando" migrations
- novos Teams: +1000% nas primeiras 24h
- CPU tinha picos de 100%
- o que acontece quando uma build é executada?
- migrations!
falha #1: "escalando" migrations
- cada site tem uma tabela de Migrations executadas
- arquivos .sql numerados
- step no agente de build
- varre lista de arquivos
- se migration não existe no banco, executa
- salva data de execução, nome e hash do arquivo na tabela
If fnColumnExists('PostVotes','IsInvalidated') = 0
Begin
Alter Table PostVotes
Add IsInvalidated bit Not Null
Constraint DF_PostVotes_IsInvalidated default(0)
End
migrations/987 - add IsInvalidated to PostVotes.sql
falha #1: "escalando" migrations
- pra cada site "novo", antes de executar os scripts, checamos se a tabela Migrations existe:
IF NOT EXISTS(SELECT 1 FROM sys.tables WHERE [name] = 'Migrations')
BEGIN
CREATE TABLE Migrations
(
Id int identity primary key,
[Filename] nvarchar(260),
[Hash] varchar(40),
[ExecutionDate] datetime,
[Duration] int
)
CREATE UNIQUE INDEX UQ_Filename ON Migrations([Filename])
CREATE UNIQUE INDEX UQ_Hash ON Migrations([Hash])
END
- o acesso a sys.tables é a parte interessante: aparentemente o sql server não gosta muito de milhares de schemas consultando essa view ao mesmo tempo, resultando em deadlocks
- solução: mover lógica pra criar tabela de Migrations pra tempo de provisionamento, não de build
falha #2: "já sei, vou usar regex"
- sintomas
- CPU dos nossos web servers topou em 100%
- servidores foram tirados de rotação pelo LB e marcados como "unhealthy"
- homepage estava demorando muito pra responder
- começamos por aqui! o que temos na homepage?
falha #2: "já sei, vou usar regex"
- a homepage e o link de uma pergunta específica tinham tempos de resposta bem acima do normal
- à primeira vista, a pergunta era inofensiva...
falha #2: "já sei, vou usar regex"
public static string TrimUnicode(this string s)
{
if (s.IsNullOrEmpty()) return s;
// see http://en.wikipedia.org/wiki/Zero-width_non-joiner
s = Regex.Replace(s, @"^[\s\u200c]+|[\s\u200c]+$", "");
return s;
}
- ... mas ela tinha 30 MIL caracteres em branco na descrição
- código usado pra "normalizar" posts e comentários
- faz "trim" de uma string, removendo inclusive caracteres em branco unicode
- mas o que acontece em strings com muitos espaços em branco no "meio"?
- backtracking!
falha #2: "já sei, vou usar regex"
- a pergunta entrou nos "hot network posts", sendo renderizada em nossa homepage
- a homepage é também usada pra health checks. oops
- a gente se auto-derrubou por 34 minutos
- solução: substituir a regex com substring
/// <summary>
/// like .Trim() but MORE AWESOME because it removes unicode fake spaces too
/// </summary>
public static string TrimUnicode(this string s)
{
if (s.IsNullOrEmpty()) return s;
var start = 0;
var len = s.Length;
while (start < len && (char.IsWhiteSpace(s[start]) || s[start] == '\u200c'))
start++;
var end = len - 1;
while (end >= start && (char.IsWhiteSpace(s[end]) || s[end] == '\u200c'))
end--;
if (start >= len || end < start)
return "";
return s.Substring(start, end - start + 1);
}
falha #3: migrando pra EF Core
- em outubro de 2018, migramos de Linq2SQL para EF Core
- sintomas:
- sites caíram!
-
no log de top queries, vimos um suspeito "select * from posts"
- (yay, SQL Server é super eficiente!)
- mesmo usando só 4% de CPU, nossos servers de banco saturaram a rede (trazendo todos os posts do banco pros servidores de aplicação), e olha que sao 2x10Gb links
falha #3: migrando pra EF Core
- uma de nossas consultas fazia um filtro com um new Guid
- Linq2SQL traduzia isso pra uma query SQL, trazendo ~50 resultados
- EF Core (via client side evaluation) não sabia traduzir pra SQL, então fazia a query sem filtro e filtrava em memória, trazendo... 140 milhões de resultados
var state =
db.SessionStates.Single(u => u.GUID == new Guid(sessionGuid));
// EF Core won't translate this inline...
var guidParam = new Guid(sessionGuid);
var state =
db.SessionStates.Single(u => u.GUID == guidParam);
falha #3: migrando pra EF Core
https://docs.microsoft.com/en-us/ef/core/querying/client-eval
falha #bônus: tamagotchi DDoS
https://github.com/StackExchange/stackegg
https://stackstatus.net/post/115305251014/outage-postmortem-march-31-2015
essa palestra é sobre o quê mesmo?
erros acontecem (e nos tornam desenvolvedores melhores!)
observabilidade é importante
cuidado com deadlocks
obrigada :)
@rla4
rla4.com
TDC 2021
By rla4
TDC 2021
- 829