ou como construímos o Stack Overflow for Teams
Roberta Arcoverde
https://stackoverflow.com/c/demo
https://stackoverflow.com
9 WEB SERVERS
4 SQL SERVERS
LIVE
HOT STANDBY
LIVE
HOT STANDBY
Stack Exchange, Meta, Talent
Stack Overflow
~350 req/s
por servidor
528 M queries/dia
498 M queries/dia
~5% CPU
imagem gentilmente cedida por Marco (@sklivvz) em http://www.slideshare.net/howtoweb/marco-cecconi-stack-overflow-architecture
spoilers: é boring
*migrando pra .NET Core
🤷♀️
(o nome original do SO for Teams era Channels)
nasce uma ideia! (sim, o screenshot é legítimo)
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 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
[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);
}
https://stackoverflow.com/help/search-inline
https://askubuntu.com/help/search-inline
https://stackoverflow.com/c/demo/help/search-inline
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, 100k times?
Escalabilidade. AG distribuídos começam a degradar rapidamente a partir de 1k bancos
Hardware e instrumentação para gerenciar milhares de bases de dados
Escalabilidade é... decente
Baixo custo de reescrita
Precisamos escrever infra de provisionamento dinâmico
basicamente: saindo de 170 para 10k+ sites
[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
// 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 will set)
// - 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);
}
}
// ...
}
rla4
roberta at stackoverflow.com
rla4.com
hipsters.tech