Re-arquitetando o Re-arquitetando o Stack Overflow Stack Overflow ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1
/whois /whois recifense programadora há 15 anos principal software developer na stack overflow co-host do hipsters.tech @rla4 2
desde 2008 50+ milhões de usuários únicos/mês 18 milhões de perguntas 27 milhões de respostas top 50 sites mais acessados do mundo 3
3k Teams criados, 50k usuários 10 meses em desenvolvimento lançado em maio/2018 equipe tinha originalmente 3 devs, agora são 7 melhor nome de time da história: Teams Team � 4
https://stackoverflow.com/ c/demo 5
https://stackoverflow.com/ c/demo 5
https://stackoverflow.com/ c/demo 5
https://stackoverflow.com/ c/demo 5
https://stackoverflow.com/ c/demo 5
https://stackoverflow.com/ c/demo 5
https://stackoverflow.com 6
https://stackoverflow.com 6
>170 sites >170 sites 7
números do dia 03/05 números do dia 03/05 278.912.108 HTTP requests 67.188.355 page views 3.506.670.995.363 bytes (3.5 TB) enviados 953.860.308 SQL queries executadas 5.250.697.564 redis hits 600.000 websockets ativos 19 ms de tempo de renderização da Question page 54.290.431 page views, ou 80% do total 123 ms de tempo de renderização geral 8
9 WEB SERVERS ~350 req/s ~5% CPU por servidor 4 SQL SERVERS Stack Overflow Stack Exchange, Meta, Talent 528 M 498 M queries/dia queries/dia LIVE HOT STANDBY LIVE HOT STANDBY 9
imagem gentilmente cedida por Marco (@sklivvz) em http://www.slideshare.net/howtoweb/marco-cecconi-stack-overflow-architecture 10
11
como? como? spoilers: é boring 12
performance performance é uma é uma feature feature 13
tech stack tech stack c# asp.net mvc* sql server dapper, ef core typescript vanilla redis *migrando pra .NET Core elasticsearch ha proxy 14
15
� ♀ 15
multi tenant application multi tenant application um único app pool para todos os sites roteado via host headers 16
17
https://nickcraver.com/blog/2016/02/03/stack-overflow-a-technical-deconstruction/ 18
Q&A pra dados Q&A pra dados privados? privados? 19
(o nome original do SO for Teams era Channels) nasce uma ideia! (sim, o screenshot é legítimo) 20
times são sites que existem dentro do Stack Overflow tratá-los como se fossem novos sites na rede, porém visíveis apenas a partir do public class Post { public class Post { public int Id { get ; } public int Id { get ; } public string Title { get ; } public string Title { get ; } ... public int ? TeamId { get ; } } ... } // reusar banco // criar novo banco // criar novo código // reusar código 21
https://stackoverflow.com/help/search-inline https://askubuntu.com/help/search-inline https://stackoverflow.com/c/demo/help/search-inline 1 [StackRoute("help/search-inline")] 2 public async Task<ActionResult> SearchInline ( string q) 3 { 4 var searchSite = GetSearchSite(); 5 var results = await searchSite.HelpPostIndex.SearchAsync(searchSite, q); 6 var sm = new SearchModel 7 { 8 SearchString = q, 9 Results = results 10 }; 11 12 return PartialView("~/Views/Help/SearchInline.cshtml", sm); 13 } 22
Modelo Modelo evitar forks, DRY, minimizar alterações no core do projeto 23
Modelo Modelo evitar forks, DRY, minimizar alterações no core do projeto Escalabilidade Escalabilidade capacity planning, o que acontece se tivermos 1k, 10k, 100k times? 23
Modelo Modelo evitar forks, DRY, minimizar alterações no core do projeto Escalabilidade Escalabilidade capacity planning, o que acontece se tivermos 1k, 10k, 100k times? Segurança Segurança default private, mudança de mindset, crash na aplicação > vazamento de dados 23
Plano A: um banco para Plano A: um banco para cada Team cada Team � � Bases isoladas entre Escalabilidade. AG Teams distribuídos começam a Dados isolados dos degradar rapidamente dados públicos a partir de 1k bancos Mínimo de alterações Hardware e no código (usar modelo instrumentação para existente pra novos gerenciar milhares de sites) bases de dados 24
Plano B: um banco para Plano B: um banco para todos os Teams todos os Teams � � Escalabilidade Sem isolamento entre Dados isolados dos Teams dados públicos Reescrever boa parte das consultas Consultas não são mais as mesmas para sites vs Teams 25
Plano C: um schema por Plano C: um schema por time no mesmo banco time no mesmo banco � � Dados isolados entre Precisamos escrever Teams infra de Dados isolados dos provisionamento dados públicos dinâmico Escalabilidade é... decente Baixo custo de reescrita 26
27
28
Escalabilidade Escalabilidade basicamente: saindo de 170 para 10k+ sites SQL Server 1 banco per-site 1 banco pra todos os Teams, 1 schema per-Team Elasticsearch 1 índice per-site 1 índice per-team, até 5k Provisionamento tarefa agendada cria sempre um buffer de 100 schemas para futuros Teams 29
Segurança Segurança onde manter os dados dos Teams? como comunicar o site público com o Team? migrar *tudo* pra lugares seguros notificações emails monitoramento internal API websockets tags 30
31
como as redes se como as redes se comunicam? comunicam? 32
Proxying Proxying Já usávamos no /jobs Requisição é "clonada" e enviada para a CFZ Response é jogada direto no stream de saída 800 LoC Por que não usar APIs/serviços? custo de serialização mais código, menos uniformidade 33
1 [StackRoute("c/{slug}")] 2 [StackRoute("c/{slug}/{*pathInfo}")] 3 public async Task<ActionResult> Proxy ( string slug) 4 { 5 if (!Current.Settings.Channels.Enabled) 6 { 7 return PageNotFound(); 8 } 9 ... 10 if (Current.Request.IsProxied()) 11 { 12 // yo dawg, I heard you like proxies so we put a proxy in your proxy 13 // so you can channel yo inner channels... Let's not allow this 14 return PageNotFound(); 15 } 16 17 var returnUrl = Current.Request.Url.PathAndQuery; 18 if (!Current.SiteChannels.Contains(channelSite.Id)) 19 { 20 // user does not have access to this channel 21 return RedirectToJoinPage(); 22 } 23 ... 24 25 return await this .BlindProxy(channelSite, path); 26 } 27 28 // BlindProxy: 29 // valida a requisição (authorization); 30 // constrói um Request; 31 // envia via HTTP para o Team app; 32 // retorna o resultado 33 // profit :D 34
1 1 [StackRoute("c/{slug}")] [StackRoute("c/{slug}")] 2 2 [StackRoute("c/{slug}/{*pathInfo}")] [StackRoute("c/{slug}/{*pathInfo}")] 3 3 public async Task<ActionResult> Proxy ( string slug) public async Task<ActionResult> Proxy ( string slug) 4 4 { { 5 5 if (!Current.Settings.Channels.Enabled) if (!Current.Settings.Channels.Enabled) 6 6 { { 7 7 return PageNotFound(); return PageNotFound(); 8 8 } } 9 9 ... ... 10 10 if (Current.Request.IsProxied()) if (Current.Request.IsProxied()) 11 11 { { 12 12 // yo dawg, I heard you like proxies so we put a proxy in your proxy // yo dawg, I heard you like proxies so we put a proxy in your proxy 13 13 // so you can channel yo inner channels... Let's not allow this // so you can channel yo inner channels... Let's not allow this 14 14 return PageNotFound(); return PageNotFound(); 15 15 } } 16 16 17 17 var returnUrl = Current.Request.Url.PathAndQuery; var returnUrl = Current.Request.Url.PathAndQuery; 18 18 if (!Current.SiteChannels.Contains(channelSite.Id)) if (!Current.SiteChannels.Contains(channelSite.Id)) 19 19 { { 20 20 // user does not have access to this channel // user does not have access to this channel 21 21 return RedirectToJoinPage(); return RedirectToJoinPage(); 22 22 } } 23 23 ... ... 24 24 25 25 return await this .BlindProxy(channelSite, path); return await this .BlindProxy(channelSite, path); 26 26 } } 27 27 28 28 // BlindProxy: // BlindProxy: 29 29 // valida a requisição (authorization); // valida a requisição (authorization); 30 30 // constrói um Request; // constrói um Request; 31 31 // envia via HTTP para o Team app; // envia via HTTP para o Team app; 32 32 // retorna o resultado // retorna o resultado 33 33 // profit :D // profit :D 34
Recommend
More recommend