Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Race Conditions em Microsserviços: Desmistificando um Problema Comum em Arquiteturas Distribuídas

Race Conditions em Microsserviços: Desmistificando um Problema Comum em Arquiteturas Distribuídas

Os microsserviços são uma realidade na arquitetura de sistemas distribuídos, porém, o aumento da complexidade desses sistemas trouxe novos desafios, em especial na garantia da integridade e consistência dos dados de negócio. Com a crescente adoção de microsserviços, problemas de concorrência, como Race Conditions e Lost Update, se tornaram mais frequentes e podem comprometer a eficiência dos seus sistemas. Nesta talk, vamos explorar as causas que levam a problemas de Race Conditions em microsserviços, assim como heurísticas para identificar e controlar situações de concorrência.

Talk apresentada na trilha de Microsserviços II no TDC CONNECTIONS 2023.

Jordi Henrique Silva

March 24, 2023
Tweet

More Decks by Jordi Henrique Silva

Other Decks in Technology

Transcript

  1. Race Conditions em Microsserviços: Desmistificando um Problema
    Comum em Arquiteturas Distribuídas

    View Slide

  2. Era dos MAINFRAMEs

    View Slide

  3. Cliente Funcionaria
    Sistema Database

    View Slide

  4. solicita
    Cliente Funcionaria
    Sistema Database

    View Slide

  5. request
    Cliente Funcionaria
    Sistema Database
    solicita

    View Slide

  6. request
    response
    Cliente Funcionaria
    Sistema Database
    solicita

    View Slide

  7. request
    response
    Cliente Funcionaria
    Sistema Database
    responde
    solicita

    View Slide

  8. Era dos
    Microsserviços

    View Slide

  9. request
    response
    Cliente Funcionaria
    Sistema Database
    responde
    Solicita

    View Slide

  10. request
    response
    Sistema Database

    View Slide

  11. request
    response
    Sistema Database

    View Slide

  12. request
    response
    Sistema Database

    View Slide

  13. request
    response
    Sistema Database

    View Slide

  14. Como o aumento de
    acesso simultâneo
    afeta os
    negócios?

    View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. View Slide

  21. View Slide

  22. View Slide

  23. View Slide

  24. View Slide

  25. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  26. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  27. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  28. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  29. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  30. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  31. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  32. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  33. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  34. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  35. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  36. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  37. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  38. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  39. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  40. E se mais de um
    usuário tentar
    reservar a mesma
    poltrona
    simultaneamente?

    View Slide

  41. View Slide

  42. João

    View Slide

  43. João Maria

    View Slide

  44. João Maria
    select p.*
    from poltrona p
    where id = 1

    View Slide

  45. João Maria
    select p.*
    from poltrona p
    where id = 1 select p.*
    from poltrona p
    where id = 1

    View Slide

  46. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    João Maria
    select p.*
    from poltrona p
    where id = 1 select p.*
    from poltrona p
    where id = 1

    View Slide

  47. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    João Maria
    select p.*
    from poltrona p
    where id = 1 select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:

    View Slide

  48. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    João Maria
    update poltrona
    set reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    select p.*
    from poltrona p
    where id = 1 select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:

    View Slide

  49. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    João Maria
    update poltrona
    set reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    select p.*
    from poltrona p
    where id = 1 select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    update poltrona
    set reservado = true,
    reservadoPara = ‘maria’
    where id = 1

    View Slide

  50. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    João Maria
    update poltrona
    set reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    select p.*
    from poltrona p
    where id = 1 select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    update poltrona
    set reservado = true,
    reservadoPara = ‘maria’
    where id = 1

    View Slide

  51. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    João Maria
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: maria
    update poltrona
    set reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    select p.*
    from poltrona p
    where id = 1 select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    update poltrona
    set reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    1 linha atualizada

    View Slide

  52. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    João Maria
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: maria
    update poltrona
    set reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    select p.*
    from poltrona p
    where id = 1 select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    update poltrona
    set reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    1 linha atualizada

    View Slide

  53. Race Conditions

    View Slide

  54. Jordi Henrique Silva
    @JORDIHOFC

    View Slide

  55. View Slide

  56. Race Conditions em Microsserviços: Desmistificando um Problema
    Comum em Arquiteturas Distribuídas

    View Slide

  57. Solução Ingênua

    View Slide

  58. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    //read
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    //process
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    //write
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  59. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    //read
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    //process
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    //write
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  60. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    //read
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    //process
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    //write
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  61. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    //read
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    //process
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    //write
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  62. read

    View Slide

  63. read

    View Slide

  64. read process

    View Slide

  65. read process

    View Slide

  66. read process write
    ANTI-PATTERN

    View Slide

  67. Soluções

    View Slide

  68. View Slide

  69. Syncronized?

    View Slide

  70. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  71. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  72. View Slide

  73. Thread 1

    View Slide

  74. Thread 1 Thread 2

    View Slide

  75. POST /poltronas/1/reservas
    Thread 1 Thread 2

    View Slide

  76. POST /poltronas/1/reservas
    Thread 1 Thread 2

    View Slide

  77. POST /poltronas/1/reservas
    Thread 1 Thread 2
    POST /poltronas/1/reservas

    View Slide

  78. POST /poltronas/1/reservas
    Thread 1 Thread 2
    POST /poltronas/1/reservas

    View Slide

  79. POST /poltronas/1/reservas
    HTTP 200
    Thread 1 Thread 2
    POST /poltronas/1/reservas

    View Slide

  80. POST /poltronas/1/reservas
    HTTP 200
    Thread 1 Thread 2
    POST /poltronas/1/reservas

    View Slide

  81. POST /poltronas/1/reservas
    HTTP 200
    HTTP 422, “Poltrona indisponível”
    Thread 1 Thread 2
    POST /poltronas/1/reservas

    View Slide

  82. View Slide

  83. View Slide

  84. View Slide

  85. View Slide

  86. Lock Distribuído?

    View Slide

  87. View Slide

  88. Você ou seu time
    domina essa tecnologia?

    View Slide

  89. Já possui alguma
    destas tecnologias na
    sua infraestrutura?

    View Slide

  90. View Slide

  91. View Slide

  92. View Slide

  93. View Slide

  94. View Slide

  95. Pessimistic Locking

    View Slide

  96. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    //RESTANTE DO CODIGO OMITIDO
    }
    }

    View Slide

  97. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id, PESSIMISTIC_WRITE)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    //RESTANTE DO CODIGO OMITIDO
    }
    }

    View Slide

  98. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id, PESSIMISTIC_WRITE)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    //RESTANTE DO CODIGO OMITIDO
    }
    }

    View Slide

  99. select p.*
    from poltrona p
    where p.id = :poltronaId
    for update

    View Slide

  100. select p.*
    from poltrona p
    where p.id = :poltronaId
    for update

    View Slide

  101. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id, PESSIMISTIC_WRITE)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  102. View Slide

  103. João

    View Slide

  104. João Maria

    View Slide

  105. João Maria
    select p.*
    from poltrona p
    where id = 1
    for update

    View Slide

  106. João Maria
    select p.*
    from poltrona p
    where id = 1
    for update
    select p.*
    from poltrona p
    where id = 1
    for update

    View Slide

  107. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    João Maria
    select p.*
    from poltrona p
    where id = 1
    for update
    select p.*
    from poltrona p
    where id = 1
    for update

    View Slide

  108. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    João Maria
    select p.*
    from poltrona p
    where id = 1
    for update
    select p.*
    from poltrona p
    where id = 1
    for update

    View Slide

  109. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    João Maria
    select p.*
    from poltrona p
    where id = 1
    for update
    select p.*
    from poltrona p
    where id = 1
    for update

    View Slide

  110. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    João Maria
    update poltrona
    set reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    select p.*
    from poltrona p
    where id = 1
    for update
    select p.*
    from poltrona p
    where id = 1
    for update

    View Slide

  111. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    João Maria
    COMMIT
    update poltrona
    set reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    select p.*
    from poltrona p
    where id = 1
    for update
    select p.*
    from poltrona p
    where id = 1
    for update

    View Slide

  112. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    João Maria
    COMMIT
    update poltrona
    set reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    select p.*
    from poltrona p
    where id = 1
    for update
    select p.*
    from poltrona p
    where id = 1
    for update

    View Slide

  113. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    João Maria
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    COMMIT
    update poltrona
    set reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    select p.*
    from poltrona p
    where id = 1
    for update
    select p.*
    from poltrona p
    where id = 1
    for update

    View Slide

  114. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id, PESSIMISTIC_WRITE)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  115. View Slide

  116. Se houver alto volume
    de requisições causando
    alta espera por acesso
    da linha?

    View Slide

  117. É possível diminuir o
    tempo do lock?

    View Slide

  118. E Se ...

    View Slide

  119. ... Trocarmos a
    estratégia de evitar
    conflitos para detectar
    conflitos?

    View Slide

  120. Optimistic Locking

    View Slide

  121. @Entity
    class Poltrona(
    @field:Column(nullable = false) val descricao: String,
    @field:Column(nullable = false) var reservadoPara: String,
    @field:Column(nullable = false) var reservado: Boolean = false
    @field:Version var version: Long
    ) {
    @Id
    @GeneratedValue
    var id: Long? = null;
    }

    View Slide

  122. @Entity
    class Poltrona(
    @field:Column(nullable = false) val descricao: String,
    @field:Column(nullable = false) var reservadoPara: String,
    @field:Column(nullable = false) var reservado: Boolean = false,
    var version: Long
    ) {
    @Id
    @GeneratedValue
    var id: Long? = null;
    }

    View Slide

  123. @Entity
    class Poltrona(
    @field:Column(nullable = false) val descricao: String,
    @field:Column(nullable = false) var reservadoPara: String,
    @field:Column(nullable = false) var reservado: Boolean = false,
    @field:Version var version: Long
    ) {
    @Id
    @GeneratedValue
    var id: Long? = null;
    }

    View Slide

  124. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id, PESSIMISTIC_WRITE)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  125. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  126. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Syncronized
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    manager.merge(poltrona);
    return ok();
    }
    }

    View Slide

  127. update poltrona
    set reservado = true,
    reservadoPara = :user
    where id = :poltronaId

    View Slide

  128. update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = :user
    where id = :poltronaId
    and version = 0

    View Slide

  129. View Slide

  130. João

    View Slide

  131. João Maria

    View Slide

  132. João Maria
    select p.*
    from poltrona p
    where id = 1

    View Slide

  133. João Maria
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  134. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    João Maria
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  135. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    João Maria
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  136. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    João Maria
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  137. Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    and version = 0
    João Maria
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  138. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    version: 1
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    and version = 0
    João Maria
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  139. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    version: 1
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    and version = 0
    João Maria
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  140. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    version: 1
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    and version = 0
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    and version = 0
    João Maria
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  141. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    version: 1
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    and version = 0
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    and version = 0
    João Maria
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  142. 1 linha atualizada
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: joao
    version: 1
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    and version = 0
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    and version = 0
    João Maria
    OptimisticLockException
    select p.*
    from poltrona p
    where id = 1
    select p.*
    from poltrona p
    where id = 1

    View Slide

  143. View Slide

  144. Como não estourar um erro na cara do usuário?!
    OptimisticLockException

    View Slide

  145. View Slide

  146. João

    View Slide

  147. João

    View Slide

  148. João
    POST /poltronas/1/reservas

    View Slide

  149. João
    POST /poltronas/1/reservas select p.*
    from poltrona p
    where id = 1

    View Slide

  150. João
    POST /poltronas/1/reservas select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0

    View Slide

  151. João
    POST /poltronas/1/reservas select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    and version = 0

    View Slide

  152. João
    POST /poltronas/1/reservas select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    and version = 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: maria
    version: 1

    View Slide

  153. João
    POST /poltronas/1/reservas select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    and version = 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: maria
    version: 1
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    and version = 0

    View Slide

  154. João
    POST /poltronas/1/reservas select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    and version = 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: maria
    version: 1
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    and version = 0
    OptimisticLockException

    View Slide

  155. João
    POST /poltronas/1/reservas select p.*
    from poltrona p
    where id = 1
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: false
    reservadoPara:
    version: 0
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘maria’
    where id = 1
    and version = 0
    Poltrona
    --------------
    id: 1,
    descriçao: A3
    reservado: true
    reservadoPara: maria
    version: 1
    update poltrona
    set version = 1,
    reservado = true,
    reservadoPara = ‘joao’
    where id = 1
    and version = 0
    OptimisticLockException
    HTTP 500

    View Slide

  156. HTTP 500
    É uma mensagem
    muito genérica

    View Slide

  157. HTTP 409 CONFLICT

    View Slide

  158. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    try {
    manager.merge(poltrona);
    }catch (ex: OptimisticLockException){
    throw HttpStatusException(CONFLICT,"Impossivel realizar a reserva, tente novamente.")
    }
    return ok();
    }
    }

    View Slide

  159. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    try {
    manager.merge(poltrona);
    }catch (ex: OptimisticLockException){
    throw HttpStatusException(CONFLICT,"Impossivel realizar a reserva, tente novamente.")
    }
    return ok();
    }
    }

    View Slide

  160. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    try {
    manager.merge(poltrona);
    }catch (ex: OptimisticLockException){
    throw HttpStatusException(CONFLICT,"Impossivel realizar a reserva, tente novamente.")
    }
    return ok();
    }
    }

    View Slide

  161. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    try {
    manager.merge(poltrona);
    }catch (ex: OptimisticLockException){
    throw HttpStatusException(CONFLICT,"Impossivel realizar a reserva, tente novamente.")
    }
    return ok();
    }
    }

    View Slide

  162. Existe uma forma mais
    elegante...

    View Slide

  163. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {
    val poltrona = manager.find(Poltrona::class.java, id)
    ?: throw HttpStatusException(NOT_FOUND, "Poltrona não existente.")
    if (poltrona.isReservada()) {
    throw HttpStatusException(UNPROCESSABLE_ENTITY, "Poltrona indisponivel")
    }
    poltrona.reservadoPara = usuario;
    poltrona.reservado = true;
    try {
    manager.merge(poltrona);
    }catch (ex: OptimisticLockException){
    throw HttpStatusException(CONFLICT,"Impossivel realizar a reserva, tente novamente.")
    }
    return ok();
    }
    }

    View Slide

  164. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {...}
    @Error
    fun errorHandler(request: HttpRequest<*>, ex: OptimisticLockException): HttpResponse<*> {
    return HttpResponse.status(CONFLICT, ex.message)
    }
    }

    View Slide

  165. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {...}
    @Error
    fun errorHandler(request: HttpRequest<*>, ex: OptimisticLockException): HttpResponse<*> {
    return HttpResponse.status(CONFLICT,"Impossivel realizar a reserva, tente novamente.")
    }
    }

    View Slide

  166. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {...}
    @Error
    fun errorHandler(request: HttpRequest<*>, ex: OptimisticLockException): HttpResponse<*> {
    return HttpResponse.status(CONFLICT,"Impossivel realizar a reserva, tente novamente.")
    }
    }

    View Slide

  167. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {...}
    @Error
    fun errorHandler(request: HttpRequest<*>, ex: OptimisticLockException): HttpResponse<*> {
    return HttpResponse.status(CONFLICT,"Impossivel realizar a reserva, tente novamente.")
    }
    }

    View Slide

  168. @Controller("/api/v1/poltronas/{id}/reservas")
    class ReservaPoltronaController(val manager: EntityManager) {
    @Transactional
    @Post
    fun reservar(@PathVariable id: Long, @QueryValue usuario: String): HttpResponse {...}
    @Error
    fun errorHandler(request: HttpRequest<*>, ex: OptimisticLockException): HttpResponse<*> {
    return HttpResponse.status(CONFLICT,"Impossivel realizar a reserva, tente novamente.")
    }
    }

    View Slide

  169. View Slide

  170. CONCLUSÃO

    View Slide

  171. read

    View Slide

  172. read process

    View Slide

  173. read process write
    ANTI-PATTERN

    View Slide

  174. Race Conditions
    LOST UPADATE

    View Slide

  175. @Syncronized

    View Slide

  176. Pessimistic Locking

    View Slide

  177. Optimistic Locking

    View Slide

  178. Outras estratégias que os
    Bancos de dados oferecem
    para lidar com problemas
    de concorrência

    View Slide

  179. Check Constraints
    Advisory Locks
    Table Locks
    Isolation Level

    View Slide

  180. Check Constraints
    Advisory Locks
    Table Locks
    Isolation Level

    View Slide

  181. Check Constraints
    Advisory Locks
    Table Locks
    Isolation Level

    View Slide

  182. Check Constraints
    Advisory Locks
    Table Locks
    Isolation Level

    View Slide

  183. Se você usa o Banco de dados ...
    ... apenas como repositório.
    VOCÊ ESTA SUB-UTILIZANDO!

    View Slide

  184. OBRIGADO!
    @JORDIHOFC

    View Slide