52
Re-arquitetando o Re-arquitetando o Stack Overflow Stack Overflow ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

Re-arquitetando oRe-arquitetando oStack OverflowStack Overflow

ou como construímos o Stack Overflow for Teams

Roberta Arcoverde1

Page 2: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

/whois /whois 

recifenseprogramadora há 15 anosprincipal softwaredeveloper na stackoverflowco-host do hipsters.tech@rla4

2

Page 3: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

desde 200850+ milhões de usuários únicos/mês18 milhões de perguntas27 milhões de respostastop 50 sites mais acessados do mundo

3

Page 4: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

3k Teams criados, 50k usuários10 meses em desenvolvimentolançado em maio/2018equipe tinha originalmente 3 devs,agora são 7

melhor nome de time da história:Teams Team �

4

Page 5: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

https://stackoverflow.com/c/demo 5

Page 6: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

https://stackoverflow.com/c/demo 5

Page 7: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

https://stackoverflow.com/c/demo 5

Page 8: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

https://stackoverflow.com/c/demo 5

Page 9: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

https://stackoverflow.com/c/demo 5

Page 10: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

https://stackoverflow.com/c/demo 5

Page 11: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

https://stackoverflow.com 6

Page 12: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

https://stackoverflow.com 6

Page 13: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

>170 sites>170 sites

7

Page 14: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

números do dia 03/05números do dia 03/05

278.912.108 HTTP requests67.188.355 page views3.506.670.995.363 bytes (3.5 TB) enviados953.860.308 SQL queries executadas5.250.697.564 redis hits600.000 websockets ativos19ms de tempo de renderização da Question page

54.290.431 page views, ou 80% do total123ms de tempo de renderização geral

8

Page 15: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

9 WEB SERVERS

4 SQL SERVERS

LIVE HOT STANDBY LIVE HOT STANDBY

9

Stack Exchange, Meta, TalentStack Overflow

~350 req/s

por servidor

528 Mqueries/dia

498 Mqueries/dia

~5% CPU

Page 16: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

imagem gentilmente cedida por Marco (@sklivvz) em http://www.slideshare.net/howtoweb/marco-cecconi-stack-overflow-architecture

10

Page 17: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

11

Page 18: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

como?como? 

spoilers: é boring12

Page 19: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

performance performance é umaé umafeaturefeature

13

Page 20: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

tech stacktech stackc#asp.net mvc*sql server

dapper, ef coretypescript

vanillarediselasticsearchha proxy

*migrando pra .NET Core

14

Page 21: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

15

Page 22: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

�♀ 

15

Page 23: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

multi tenant applicationmulti tenant application

um único app pool paratodos os sitesroteado via host headers

16

Page 24: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

17

Page 25: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

https://nickcraver.com/blog/2016/02/03/stack-overflow-a-technical-deconstruction/ 18

Page 26: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

Q&A pra dadosQ&A pra dadosprivados?privados?

19

Page 27: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

(o nome original do SO for Teams era Channels)

nasce uma ideia! (sim, o screenshot é legítimo)20

Page 28: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

times são sites que existem

dentro do Stack Overflowtratá-los como se fossem novos sites narede, porém visíveis apenas a partir do 

public class Post { public int Id { get; } public string Title { get; } public int? TeamId { get; } ... } // reusar banco // criar novo código

public class Post { public int Id { get; } public string Title { get; } ... } // criar novo banco // reusar código

21

Page 29: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

[StackRoute("help/search-inline")]public async Task<ActionResult> SearchInline(string q){ var searchSite = GetSearchSite(); var results = await searchSite.HelpPostIndex.SearchAsync(searchSite, q); var sm = new SearchModel { SearchString = q, Results = results }; return PartialView("~/Views/Help/SearchInline.cshtml", sm);}

123456789

10111213

https://stackoverflow.com/help/search-inlinehttps://askubuntu.com/help/search-inlinehttps://stackoverflow.com/c/demo/help/search-inline

22

Page 30: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

ModeloModeloevitar forks, DRY, minimizar alterações no core do projeto

23

Page 31: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

ModeloModelo

EscalabilidadeEscalabilidade

evitar forks, DRY, minimizar alterações no core do projeto

capacity planning, o que acontece se tivermos 1k, 10k, 100ktimes?

23

Page 32: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

ModeloModelo

SegurançaSegurança

EscalabilidadeEscalabilidade

evitar forks, DRY, minimizar alterações no core do projeto

default private, mudança de mindset, crash na aplicação >vazamento de dados

capacity planning, o que acontece se tivermos 1k, 10k, 100ktimes?

23

Page 33: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

��

Bases isoladas entreTeamsDados isolados dosdados públicosMínimo de alteraçõesno código (usar modeloexistente pra novossites)

��

Escalabilidade. AGdistribuídos começam adegradar rapidamentea partir de 1k bancosHardware einstrumentação paragerenciar milhares debases de dados

Plano A: um banco paraPlano A: um banco paracada Teamcada Team

24

Page 34: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

��

EscalabilidadeDados isolados dosdados públicos

��

Sem isolamento entreTeamsReescrever boa partedas consultasConsultas não são maisas mesmas para sites vsTeams

Plano B: um banco paraPlano B: um banco paratodos os Teamstodos os Teams

25

Page 35: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

��

Dados isolados entreTeamsDados isolados dosdados públicosEscalabilidade é...decenteBaixo custo de reescrita

��

Precisamos escreverinfra deprovisionamentodinâmico

Plano C: um schema porPlano C: um schema portime no mesmo bancotime no mesmo banco

26

Page 36: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

27

Page 37: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

28

Page 38: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

basicamente: saindo de 170 para 10k+ sites

SQL Server1 banco per-site1 banco pra todos os Teams, 1 schema per-Team

Elasticsearch1 índice per-site1 índice per-team, até 5k

Provisionamentotarefa agendada cria sempre um buffer de 100schemas para futuros Teams

EscalabilidadeEscalabilidade

29

Page 39: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

onde manter os dados dos Teams?como comunicar o site público com o Team?migrar *tudo* pra lugares seguros

notificaçõesemailsmonitoramentointernal APIwebsocketstags

SegurançaSegurança

30

Page 40: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

31

Page 41: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

como as redes secomo as redes secomunicam?comunicam?

32

Page 42: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

ProxyingProxyingJá usávamos no /jobsRequisição é "clonada" e enviada para a CFZResponse é jogada direto no stream de saída800 LoCPor que não usar APIs/serviços?

custo de serializaçãomais código, menos uniformidade

33

Page 43: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

[StackRoute("c/{slug}")][StackRoute("c/{slug}/{*pathInfo}")]public async Task<ActionResult> Proxy(string slug){ if (!Current.Settings.Channels.Enabled) { return PageNotFound(); } ... if (Current.Request.IsProxied()) { // yo dawg, I heard you like proxies so we put a proxy in your proxy // so you can channel yo inner channels... Let's not allow this return PageNotFound(); } var returnUrl = Current.Request.Url.PathAndQuery; if (!Current.SiteChannels.Contains(channelSite.Id)) { // user does not have access to this channel return RedirectToJoinPage(); } ... return await this.BlindProxy(channelSite, path);} // BlindProxy: // valida a requisição (authorization);// constrói um Request;// envia via HTTP para o Team app;// retorna o resultado// profit :D

123456789

101112131415161718192021222324252627282930313233

34

Page 44: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

[StackRoute("c/{slug}")][StackRoute("c/{slug}/{*pathInfo}")]public async Task<ActionResult> Proxy(string slug){ if (!Current.Settings.Channels.Enabled) { return PageNotFound(); } ... if (Current.Request.IsProxied()) { // yo dawg, I heard you like proxies so we put a proxy in your proxy // so you can channel yo inner channels... Let's not allow this return PageNotFound(); } var returnUrl = Current.Request.Url.PathAndQuery; if (!Current.SiteChannels.Contains(channelSite.Id)) { // user does not have access to this channel return RedirectToJoinPage(); } ... return await this.BlindProxy(channelSite, path);} // BlindProxy: // valida a requisição (authorization);// constrói um Request;// envia via HTTP para o Team app;// retorna o resultado// profit :D

123456789

101112131415161718192021222324252627282930313233

[StackRoute("c/{slug}")][StackRoute("c/{slug}/{*pathInfo}")]

12

public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9 if (Current.Request.IsProxied())10 {11 // yo dawg, I heard you like proxies so we put a proxy in your proxy12 // so you can channel yo inner channels... Let's not allow this13 return PageNotFound();14 }15 16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24 return await this.BlindProxy(channelSite, path);25}26 27// BlindProxy: 28// valida a requisição (authorization);29// constrói um Request;30// envia via HTTP para o Team app;31// retorna o resultado32// profit :D33

34

Page 45: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

[StackRoute("c/{slug}")][StackRoute("c/{slug}/{*pathInfo}")]public async Task<ActionResult> Proxy(string slug){ if (!Current.Settings.Channels.Enabled) { return PageNotFound(); } ... if (Current.Request.IsProxied()) { // yo dawg, I heard you like proxies so we put a proxy in your proxy // so you can channel yo inner channels... Let's not allow this return PageNotFound(); } var returnUrl = Current.Request.Url.PathAndQuery; if (!Current.SiteChannels.Contains(channelSite.Id)) { // user does not have access to this channel return RedirectToJoinPage(); } ... return await this.BlindProxy(channelSite, path);} // BlindProxy: // valida a requisição (authorization);// constrói um Request;// envia via HTTP para o Team app;// retorna o resultado// profit :D

123456789

101112131415161718192021222324252627282930313233

[StackRoute("c/{slug}")][StackRoute("c/{slug}/{*pathInfo}")]

12

public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9 if (Current.Request.IsProxied())10 {11 // yo dawg, I heard you like proxies so we put a proxy in your proxy12 // so you can channel yo inner channels... Let's not allow this13 return PageNotFound();14 }15 16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24 return await this.BlindProxy(channelSite, path);25}26 27// BlindProxy: 28// valida a requisição (authorization);29// constrói um Request;30// envia via HTTP para o Team app;31// retorna o resultado32// profit :D33

if (Current.Request.IsProxied()) { // yo dawg, I heard you like proxies so we put a proxy in your proxy // so you can channel yo inner channels... Let's not allow this return PageNotFound(); }

[StackRoute("c/{slug}")]1[StackRoute("c/{slug}/{*pathInfo}")]2public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9

101112131415

16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24 return await this.BlindProxy(channelSite, path);25}26 27// BlindProxy: 28// valida a requisição (authorization);29// constrói um Request;30// envia via HTTP para o Team app;31// retorna o resultado32// profit :D33

34

Page 46: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

[StackRoute("c/{slug}")][StackRoute("c/{slug}/{*pathInfo}")]public async Task<ActionResult> Proxy(string slug){ if (!Current.Settings.Channels.Enabled) { return PageNotFound(); } ... if (Current.Request.IsProxied()) { // yo dawg, I heard you like proxies so we put a proxy in your proxy // so you can channel yo inner channels... Let's not allow this return PageNotFound(); } var returnUrl = Current.Request.Url.PathAndQuery; if (!Current.SiteChannels.Contains(channelSite.Id)) { // user does not have access to this channel return RedirectToJoinPage(); } ... return await this.BlindProxy(channelSite, path);} // BlindProxy: // valida a requisição (authorization);// constrói um Request;// envia via HTTP para o Team app;// retorna o resultado// profit :D

123456789

101112131415161718192021222324252627282930313233

[StackRoute("c/{slug}")][StackRoute("c/{slug}/{*pathInfo}")]

12

public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9 if (Current.Request.IsProxied())10 {11 // yo dawg, I heard you like proxies so we put a proxy in your proxy12 // so you can channel yo inner channels... Let's not allow this13 return PageNotFound();14 }15 16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24 return await this.BlindProxy(channelSite, path);25}26 27// BlindProxy: 28// valida a requisição (authorization);29// constrói um Request;30// envia via HTTP para o Team app;31// retorna o resultado32// profit :D33

if (Current.Request.IsProxied()) { // yo dawg, I heard you like proxies so we put a proxy in your proxy // so you can channel yo inner channels... Let's not allow this return PageNotFound(); }

[StackRoute("c/{slug}")]1[StackRoute("c/{slug}/{*pathInfo}")]2public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9

101112131415

16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24 return await this.BlindProxy(channelSite, path);25}26 27// BlindProxy: 28// valida a requisição (authorization);29// constrói um Request;30// envia via HTTP para o Team app;31// retorna o resultado32// profit :D33

return await this.BlindProxy(channelSite, path);

[StackRoute("c/{slug}")]1[StackRoute("c/{slug}/{*pathInfo}")]2public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9 if (Current.Request.IsProxied())10 {11 // yo dawg, I heard you like proxies so we put a proxy in your proxy12 // so you can channel yo inner channels... Let's not allow this13 return PageNotFound();14 }15 16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24

25}26 27// BlindProxy: 28// valida a requisição (authorization);29// constrói um Request;30// envia via HTTP para o Team app;31// retorna o resultado32// profit :D33

34

Page 47: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

[StackRoute("c/{slug}")][StackRoute("c/{slug}/{*pathInfo}")]public async Task<ActionResult> Proxy(string slug){ if (!Current.Settings.Channels.Enabled) { return PageNotFound(); } ... if (Current.Request.IsProxied()) { // yo dawg, I heard you like proxies so we put a proxy in your proxy // so you can channel yo inner channels... Let's not allow this return PageNotFound(); } var returnUrl = Current.Request.Url.PathAndQuery; if (!Current.SiteChannels.Contains(channelSite.Id)) { // user does not have access to this channel return RedirectToJoinPage(); } ... return await this.BlindProxy(channelSite, path);} // BlindProxy: // valida a requisição (authorization);// constrói um Request;// envia via HTTP para o Team app;// retorna o resultado// profit :D

123456789

101112131415161718192021222324252627282930313233

[StackRoute("c/{slug}")][StackRoute("c/{slug}/{*pathInfo}")]

12

public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9 if (Current.Request.IsProxied())10 {11 // yo dawg, I heard you like proxies so we put a proxy in your proxy12 // so you can channel yo inner channels... Let's not allow this13 return PageNotFound();14 }15 16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24 return await this.BlindProxy(channelSite, path);25}26 27// BlindProxy: 28// valida a requisição (authorization);29// constrói um Request;30// envia via HTTP para o Team app;31// retorna o resultado32// profit :D33

if (Current.Request.IsProxied()) { // yo dawg, I heard you like proxies so we put a proxy in your proxy // so you can channel yo inner channels... Let's not allow this return PageNotFound(); }

[StackRoute("c/{slug}")]1[StackRoute("c/{slug}/{*pathInfo}")]2public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9

101112131415

16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24 return await this.BlindProxy(channelSite, path);25}26 27// BlindProxy: 28// valida a requisição (authorization);29// constrói um Request;30// envia via HTTP para o Team app;31// retorna o resultado32// profit :D33

return await this.BlindProxy(channelSite, path);

[StackRoute("c/{slug}")]1[StackRoute("c/{slug}/{*pathInfo}")]2public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9 if (Current.Request.IsProxied())10 {11 // yo dawg, I heard you like proxies so we put a proxy in your proxy12 // so you can channel yo inner channels... Let's not allow this13 return PageNotFound();14 }15 16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24

25}26 27// BlindProxy: 28// valida a requisição (authorization);29// constrói um Request;30// envia via HTTP para o Team app;31// retorna o resultado32// profit :D33

// BlindProxy: // valida a requisição (authorization);// constrói um Request;// envia via HTTP para o Team app;// retorna o resultado// profit :D

[StackRoute("c/{slug}")]1[StackRoute("c/{slug}/{*pathInfo}")]2public async Task<ActionResult> Proxy(string slug)3{4 if (!Current.Settings.Channels.Enabled)5 {6 return PageNotFound();7 } 8 ...9 if (Current.Request.IsProxied())10 {11 // yo dawg, I heard you like proxies so we put a proxy in your proxy12 // so you can channel yo inner channels... Let's not allow this13 return PageNotFound();14 }15 16 var returnUrl = Current.Request.Url.PathAndQuery;17 if (!Current.SiteChannels.Contains(channelSite.Id))18 {19 // user does not have access to this channel20 return RedirectToJoinPage();21 }22 ...23 24 return await this.BlindProxy(channelSite, path);25}26 27

282930313233

34

Page 48: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

// No, you can't: // - Use a CookieCollection (it'll get headers, but not pass them here) // - Set the Set-Cookie header on the response (ASP.Net strips it) // - Set an additional Set-Cookie (also stripped) // - Take the raw header and pass it (comma delimited, only the first cookie wil // - Use Headers.GetValues(string) (it screws up on commas) // - Maintain your sanity working with ASP.Net and cookie headers // Fun fact: half of the cookie BS here is supporting IIS6 and IE5. Not kidding. if (cResponse.Headers["Set-Cookie"].HasValue()) { var nvc = cResponse.Headers; var result = new List<string>(); for (var i = 0; i < nvc.Count; i++) { if (nvc.GetKey(i) == "Set-Cookie") { // Don't ask. You'll cry. var vals = nvc.GetValues(i); if (vals != null) result.AddRange(vals); } } // ... } 35

Page 49: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

liçõesliçõesentenda seus cenários de escalabilidadequando não souber: capacity planningsegurança vai além de proteger dados de acessoexterno

36

Page 50: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

outras palestrasoutras palestrasinstrumentação

adaptamos todos os nossos sistemas demonitoramento pra incluir Teams

proxy v2protobufgrpcstructured model

single sign-onre-arquitetando o modelo de autenticação eautorização

modelo de segurançadados (perguntas, respostas, tags)metadados (traffic logs, IPs, urls)external endpoints (ads, APIs, emails) 37

Page 51: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

obrigada!obrigada!

rla4roberta at stackoverflow.comrla4.comhipsters.tech 38

Page 52: Re-arquite tando o Stack Overflo w - QConSP · Re-arquite tando o Stack Overflo w ou como construímos o Stack Overflow for Teams Roberta Arcoverde 1

instância privada, standalone do StackOverflowSLA, priority supportsingle sign-onon premise ou Azurereleases trimestraiscompletamente customizávelapropriado para grandes empresas$$$

39