Skip to content

Instantly share code, notes, and snippets.

@rafaelpontezup
Created October 31, 2022 17:01
Show Gist options
  • Save rafaelpontezup/8948f111e94905324ac6920a27f1fd5e to your computer and use it in GitHub Desktop.
Save rafaelpontezup/8948f111e94905324ac6920a27f1fd5e to your computer and use it in GitHub Desktop.
Chapter Stackspot: Race conditions, locking e bancos de dados relacionais

Race conditions, locking e bancos de dados relacionais

Pretendo apresentar um código com uma lógica de negócio simples que funciona bem na perspectiva do negócio mas que quebra miseravelmente em ambientes minimamente concorrentes. Para isso, vou demonstrar através de testes de integração como identificar o Race Condition no código e principalmente como resolvê-lo através de mecanismos de locking e Isolation Level do seu banco de dados;

O que vamos discutir

  1. Por que estudamos esse assunto mais a fundo?
  2. Entendendo o problema
    • 2.1. Nossa lógica de negócio
    • 2.2. Cobrindo a lógica com testes de integração
    • 2.3. Nossa lógica funciona em ambientes concorrentes?
    • 2.4. Deu xabu!! 😱😱 Race Condition do tipo Lost Update
    • 2.5. Entendendo o que aconteceu
    • 2.6. Cuidado com anti-pattern Read-Modify-Write
  3. Tentando resolver o problema com soluções ingênuas
    • 3.1. Basta um @Transactional e pronto! [NaiveATMService]
    • 3.2. Concorrência? Moleza, a gente resolve com synchronized no método! [NaiveJavaSynchronizedATMService]
  4. O que o banco de dados pode fazer por mim? O poder do ACID!
    • 4.1. Banco é mais do que um "repositório de dados"
    • 4.2. Pessimistic Locking: Mais simples que isso não dá! [PessimisticLockingATMService]
    • 4.3. Optimistic Locking: Deixa a aplicação cuidar disso para mim [OptimisticLockingATMService]
    • 4.4. Constraints CHECK(): as vezes tudo se resolve com uma constraint [AtomicUpdateWithCheckConstraintATMService]
    • 4.5. Atomic Update: levando a lógica pro banco
      • 4.5.1. Escreva antes de validar: AtomicUpdateWithPostValidationATMService
      • 4.5.2. ...se possivel evite round-trips: AtomicUpdateWithReturningClauseATMService
      • 4.5.3. ...se possivel faz tudo no banco: AtomicUpdateWithWhereClauseATMService
    • 4.6. Isolation Levels: indo além do básico com concorrência e banco de dados
      • 4.6.1. Conhecendo os níveis de isolamento:
        • READ_UNCOMMITTED: eu faço leituras sujas;
        • READ_COMMITTED: eu só leio o que foi comitado;
        • REPEATABLE_READ: o que eu li no começo é verdade até o fim;
        • SERIALIZABLE: eu enxergo como se tudo acontecesse de forma sequencial
      • 4.6.2. Qual o default? READ_UNCOMMITTED para maioria dos bancos
      • 4.6.3. REPEATABLE_READ: respeitando o ciclo Read-Mofify-Write [RepeatableReadIsolationLevelATMService]
      • 4.6.4. SERIALIZABLE: as vezes ser o mais restrito eh o caminho [SerializableIsolationLevelATMService]
      • 4.6.5. Nada vem de graça. Tradeoffs que chama né?
  5. Que mané banco de dados
    • 5.1. Usa um lock distribuído com Redis ou Mongo, ou mete numa fila com Kafka ou RabbitMQ. Ou, como gosto de pensar: "DevOps pra que né?"
    • 5.2. O banco relacional é mais do que um repositório de dados
    • 5.3. Distributed Lock: Postgres e seu Advisory Lock
  6. Concluindo
    • O banco de dados relacional é uma engine completa de concorrência
    • Você não precisa aprender tudo isso, mas pode ser bastante útil em sistemas criticos e com alta-concorrência
@rafaelpontezup
Copy link
Author

Alguns artigos:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment