Uma linha de configuração
A troca automática de papéis é ligada por uma única linha, e o tamanho dessa linha engana: ela muda com qual banco cada requisição da aplicação conversa.
config.active_record.database_selector = { delay: 2.seconds }
Esta aplicação não define essa linha, e o curso de nível Sênior "Arquitetura de banco de dados: réplicas" explicou por quê: não existe réplica para onde trocar. Mesmo assim, vale entender a maquinaria, porque no momento em que uma réplica existir essa linha é o caminho mais curto para usá-la, e o caminho mais curto nem sempre é o mais seguro.
O que ela insere
A linha instala um middleware Rack que envolve cada requisição. Todo o trabalho dele, tirado do ActiveRecord::Middleware::DatabaseSelector:
response = if resolver.reading_request?(request)
resolver.read(&blk)
else
resolver.write(&blk)
end
resolver.update_context(response)
São três peças móveis, e vale nomeá-las separadamente, porque cada uma pode ser trocada sozinha.
O contexto lembra alguma coisa sobre essa pessoa entre uma requisição e outra. Por padrão, ele é a sessão.
O resolvedor decide, a partir da requisição e do contexto, se essa requisição lê da réplica.
O middleware liga os dois e envolve a requisição no papel escolhido.
flowchart TB
R["uma requisição chega"] --> M["middleware DatabaseSelector"]
M --> C["contexto: o que lembramos desta sessão?"]
M --> S["resolvedor: leitura ou escrita? réplica ou primário?"]
C --> S
S --> W["a requisição roda dentro de connected_to(role: ...)"]
W --> U["update_context: registra se esta requisição escreveu"]
A decisão inteira são duas perguntas
Esta é uma requisição de leitura? O verbo HTTP responde, e a lição 2 trata disso.
Esta sessão escreveu há pouco? Um timestamp na sessão responde, e a lição 3 trata disso.
Essa é a política inteira. Não há inspeção de query, nem noção de tabela, nem configuração por modelo. É uma regra prática sobre HTTP aplicada do mesmo jeito a todo endpoint da aplicação, inclusive aos endpoints escritos depois que ela foi ligada.
Por que ela é opcional
O Rails entrega isso desligado, e essa é uma afirmação proposital: o framework não vai supor que as suas requisições GET podem ser servidas por uma réplica, porque ele não tem como saber.
Compare com a forma explícita daquele curso:
ActiveRecord::Base.connected_to(role: :reading) do
SomeReport.generate
end
Esse bloco é uma afirmação sobre um código que você está vendo. O middleware é uma afirmação sobre um código que você ainda não leu, e a diferença entre as duas coisas é o assunto deste curso.
O que você configura ao definir a linha
O delay é o único botão, e ele é uma duração:
{ delay: 2.seconds }
É por quanto tempo uma sessão fica presa ao primário depois de escrever. Se for baixo demais, as pessoas leem dados desatualizados depois de salvar. Se for alto demais, a réplica fica sem uso para quem acabou de enviar um formulário.
Repare no que você não consegue configurar aqui: quais requisições são leituras. O resolvedor fixa isso, e mudar exige fornecer o seu próprio. A lição 6 trata disso, e essa costuma ser a resposta certa com mais frequência do que o botão delay dá a entender.
Pontos-chave
config.active_record.database_selector = { delay: 2.seconds }instala um middleware que envolve cada requisição.- São três peças trocáveis: um contexto que lembra, um resolvedor que decide e um middleware que liga os dois.
- A política são duas perguntas: esta é uma requisição de leitura, e esta sessão escreveu há pouco?
- Ela vem desligada porque o Rails não tem como saber se as suas requisições
GETsão seguras numa réplica. - O
delayé o único botão. Quais requisições contam como leitura fica fixo, a menos que você forneça o seu próprio resolvedor.
Continue com a aula 2
O verbo é o sinal
O resolvedor trata GET e HEAD como leituras, então é preciso auditar antes as requisições GET que escrevem.
Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.