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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/6101573/pasted-from-clipboard.png)
por que falar sobre insucessos
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8370479/pasted-from-clipboard.png)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/6071933/pasted-from-clipboard.png)
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/](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8376015/pasted-from-clipboard.png)
https://opserver.github.io/Opserver/
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8376018/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8376020/pasted-from-clipboard.png)
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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8376039/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8378457/pasted-from-clipboard.png)
falha #1: "escalando" migrations
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8370510/pasted-from-clipboard.png)
falha #1: "escalando" migrations
- novos Teams: +1000% nas primeiras 24h
- CPU tinha picos de 100%
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8375875/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8375890/pasted-from-clipboard.png)
- 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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8370547/pasted-from-clipboard.png)
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?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8374481/pasted-from-clipboard.png)
falha #2: "já sei, vou usar regex"
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8376179/pasted-from-clipboard.png)
- 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
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8374651/pasted-from-clipboard.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8377928/pasted-from-clipboard.png)
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
![As a general rule, Entity Framework Core attempts to evaluate a query on the server as much as possible. EF Core converts parts of the query into parameters, which it can evaluate on the client side. The rest of the query (along with the generated parameters) is given to the database provider to determine the equivalent database query to evaluate on the server. EF Core supports partial client evaluation in the top-level projection (essentially, the last call to Select()). If the top-level projection in the query can't be translated to the server, EF Core will fetch any required data from the server and evaluate remaining parts of the query on the client. If EF Core detects an expression, in any place other than the top-level projection, which can't be translated to the server, then it throws a runtime exception. See How queries work to understand how EF Core determines what can't be translated to server.](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8374625/pasted-from-clipboard.png)
https://docs.microsoft.com/en-us/ef/core/querying/client-eval
falha #bônus: tamagotchi DDoS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/184132/images/8370583/pasted-from-clipboard.png)
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
- 757