ActiveRecord::Base.transaction: todas as suas escritas, ou nenhuma

Prévia grátis 3 min de leitura

Uma transação de que os cursos anteriores dependiam, sem escrever nenhuma

ActiveRecord: migrations multi-tabela mostrou que o Rails envolve o change inteiro de uma migration comum numa transação de banco, e várias chamadas create_table entram todas ou nenhuma entra. Ninguém escreveu essa transação à mão; o Rails a forneceu. Este curso trata da mesma garantia escrita de forma explícita no código da aplicação. Ela serve para quando mais de uma escrita relacionada precisa dar certo ou falhar junto e nada fornece a transação por você.

O código deste app, fazendo isso

Enrollment::CompleteLessonService é o serviço que roda quando um aluno termina uma aula:

# app/services/enrollment/complete_lesson_service.rb (excerpt)
completion = ::Enrollment::LessonCompletion.new(user: user, lesson: lesson)

ActiveRecord::Base.transaction do
  return failure(next_lesson, completion.errors.full_messages) unless completion.save

  xp_before = ::Enrollment::ExperiencePoint.total_for(user.id)

  lesson_xp = ::Enrollment::AwardXpService.call(
    user: user, source: "lesson_completion", course: lesson.course, idempotency_key: "lesson_completion:#{lesson.id}"
  )
  ::Enrollment::UpdateStreakService.call(user: user)
  course_result = ::Enrollment::CompleteCourseService.call(user: user, course: lesson.course)
  # ... then EvaluateUnlocksService awards any new badges
end

Uma chamada produz uma linha de LessonCompletion, uma concessão de XP, uma atualização de sequência, uma possível conclusão de curso e possíveis badges. São quatro ou cinco escritas separadas em tabelas separadas. Sem uma transação em volta delas, uma falha no meio poderia deixar um aluno com XP e nenhuma conclusão registrada, ou com uma conclusão sem XP. Uma conexão de banco que cai faria isso, e um bug sem relação num serviço posterior também. Nada no app saberia consertar esse estado.

O que a garantia cobre

ActiveRecord::Base.transaction do ... end é uma chamada, e o que ela compra é específico: toda escrita dentro do bloco se torna durável junto, no mesmo momento, ou nenhuma se torna. As transações de banco em que os cursos de migrations se apoiaram para mudanças de schema dão às linhas comuns a mesma propriedade de tudo ou nada. Ou todas as escritas de Enrollment::CompleteLessonService entram no instante em que o bloco termina sem erro, ou nenhuma entra. Se algo lá dentro der errado, o banco fica como se o bloco nunca tivesse rodado.

O que "dar errado" quer dizer aqui

Essa última frase esconde o resto do curso. Nem todo jeito de um bloco terminar cedo dispara o rollback. A próxima aula testa quais disparam, e o código acima tem uma linha, return failure(...) unless completion.save, bem na fronteira entre os dois.

Pontos-chave

  • ActiveRecord: migrations multi-tabela dependia dessa garantia, fornecida de forma automática, para mudanças de schema; este curso é o mesmo mecanismo escrito à mão no código da aplicação.
  • O Enrollment::CompleteLessonService deste app envolve várias escritas separadas (uma conclusão de aula, XP, uma atualização de sequência, uma conclusão de curso, badges) num único bloco ActiveRecord::Base.transaction do ... end.
  • A garantia é de tudo ou nada: toda escrita dentro do bloco se torna durável junto. Se algo lá dentro falhar, o banco fica como se o bloco nunca tivesse rodado.
  • Nem todo jeito de um bloco terminar conta como falha. Essa distinção, e a linha do código deste app que fica em cima dela, é a próxima aula.
Aula 1 de 7 · grátis

Continue com a aula 2

O que dispara um rollback

Testa três jeitos de sair de um bloco de transação: uma exceção desfaz tudo, e um return simples grava.

Com uma conta grátis você também tem os quizzes, seu progresso e suas anotações, e a discussão.

Digite para buscar...