$30 off During Our Annual Pro Sale. View Details »

Distributed Scheduling com Spring Boot: os desafios e percalços de implementar um job em background

Distributed Scheduling com Spring Boot: os desafios e percalços de implementar um job em background

Cedo ou tarde um desenvolvedor(a) implementará seu primeiro job em background, o que geralmente é uma tarefa simples para maioria dos sistemas pode se tornar um pesadelo em sistemas que precisam lidar com grande volumetria de dados, alta performance e paralelismo. Cenários como esses escondem diversos problemas na qual a maioria dos desenvolvedores não está acostumado, como volume de dados, falhas na rede, reprocessamento indevido, sobrecarga no banco de dados, erros de estouro de memória até indisponibilidade de todo o sistema.

Lidar com boa parte desses problemas não requer tecnologias e serviços da moda (hype), mas sim fundamentos sólidos em sistemas distribuídos. Nessa talk, pretendo apresentar como um desenvolvedor(a) experiente implementa um job em background levando em conta os principais percalços e desenha uma solução para escala horizontal ao mesmo tempo em que tira proveito das tecnologias adotadas pelo time de desenvolvimento.

Se você acredita que um job em background é uma tarefa simples, então essa talk é para você!

(GRAVAÇÃO: https://youtu.be/I_kEO_HPfBU?list=PLHMMERsvy9EyWQPru4SrJAYHEGKfkjRgP&t=130)

Rafael Ponte

August 25, 2021
Tweet

More Decks by Rafael Ponte

Other Decks in Technology

Transcript

  1. DISTRIBUTED SCHEDULING | Spring Boot
    os desa
    fi
    os e percalços de implementar um job em background

    View Slide

  2. View Slide

  3. Maaaaas implementar
    um job em background…

    View Slide

  4. View Slide

  5. https://deniseyu.io/art/

    View Slide

  6. Computação paralela e
    distribuída

    View Slide

  7. Cadê meu
    cartão?

    View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. db
    servidor

    View Slide

  12. db
    servidor

    View Slide

  13. db
    servidor

    View Slide

  14. db
    servidor

    View Slide

  15. db
    servidor

    View Slide

  16. db
    servidor

    View Slide

  17. db
    servidor

    View Slide

  18. db
    servidor

    View Slide

  19. db
    servidor

    View Slide

  20. db
    servidor

    View Slide

  21. Como agendar
    tarefas com
    Spring Boot?

    View Slide

  22. @Component


    public class OneJob {


    @Scheduled(fixedDelay = 60_000)


    public void runQuiteOften() {


    // lógica do job vai aqui


    }


    }

    View Slide

  23. @Component


    public class OneJob {


    @Scheduled(fixedDelay = 60_000)


    public void runQuiteOften() {


    // lógica do job vai aqui


    }


    }

    View Slide

  24. @Component


    public class OneJob {


    @Scheduled(fixedDelay = 60_000)


    public void runQuiteOften() {


    // lógica do job vai aqui


    }


    }

    View Slide

  25. @Component


    public class OneJob {


    @Scheduled(fixedDelay = 60_000)


    public void runQuiteOften() {


    // lógica do job vai aqui


    }


    }

    View Slide

  26. @Component


    public class OneJob {




    public void runQuiteOften() {


    // lógica do job vai aqui


    }


    }

    View Slide

  27. @Component


    public class OneJob {


    @Scheduled


    public void runQuiteOften() {


    // lógica do job vai aqui


    }


    }

    View Slide

  28. @Component


    public class OneJob {


    @Scheduled(fixedDelay = 60_000)


    public void runQuiteOften() {


    // lógica do job vai aqui


    }


    }

    View Slide

  29. @Component


    public class OneJob {


    @Scheduled(fixedDelay = 60_000)


    public void runQuiteOften() {


    // lógica do job vai aqui


    }


    }

    View Slide

  30. E nossa tarefa?

    View Slide

  31. db
    servidor

    View Slide

  32. db
    servidor

    View Slide

  33. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  34. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  35. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  36. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  37. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  38. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  39. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  40. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  41. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  42. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  43. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  44. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  45. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  46. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  47. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  48. Solução
    Ingênua

    View Slide

  49. Rafael Ponte


    @rponte

    View Slide

  50. View Slide

  51. Fortaleza - Terra do Sol

    View Slide

  52. View Slide

  53. DISTRIBUTED SCHEDULING | Spring Boot
    os desa
    fi
    os e percalços de implementar um job em background

    View Slide

  54. Solução
    Ingênua

    View Slide

  55. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  56. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  57. select p.*


    from proposal p


    where p.status = 'ELIGIBLE'


    order by p.created_at asc


    View Slide

  58. E SE…

    View Slide

  59. …volume de
    dados crescer?

    View Slide

  60. View Slide

  61. View Slide

  62. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  63. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findAllByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  64. @Repository


    public interface ProposalRepository extends JpaRepository {


    public List findAllByStatusOrderByCreatedAtAsc(ProposalStatus status);


    }

    View Slide

  65. @Repository


    public interface ProposalRepository extends JpaRepository {


    public List findAllByStatusOrderByCreatedAtAsc(ProposalStatus status);


    }

    View Slide

  66. @Repository


    public interface ProposalRepository extends JpaRepository {


    public List findAllByStatusOrderByCreatedAtAsc(ProposalStatus status);


    }

    View Slide

  67. select p.*


    from proposal p


    where p.status = 'ELIGIBLE'


    order by p.created_at asc


    View Slide

  68. List findAllByStatusOrderByCreatedAtAsc(...);


    View Slide

  69. List findAllByStatusOrderByCreatedAtAsc(...);


    View Slide

  70. List findTop50ByStatusOrderByCreatedAtAsc(...);


    View Slide

  71. select p.*


    from proposal p


    where p.status = 'ELIGIBLE'


    order by p.created_at asc


    View Slide

  72. select p.*


    from proposal p


    where p.status = 'ELIGIBLE'


    order by p.created_at asc


    limit 50

    View Slide

  73. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  74. Mas…

    View Slide

  75. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }

    View Slide

  76. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }
    o loop vai
    encerrar muito
    CEDO !

    View Slide

  77. @Component


    public class AttachCardsToProposalsJob {


    private CardsClient cardsClient;


    private CardRepository cardRepository;


    private ProposalRepository proposalRepository;


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }
    Só vai rodar no
    próximo
    agendamento!

    View Slide

  78. while(true)

    View Slide

  79. @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    proposals.forEach(proposal -> {


    // processa cada proposta


    });


    }

    View Slide

  80. @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    // processa cada proposta


    });


    }


    }

    View Slide

  81. @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    // processa cada proposta


    });


    }


    }

    View Slide

  82. @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    // processa cada proposta


    });


    }


    }

    View Slide

  83. @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    // processa cada proposta


    });


    }


    }

    View Slide

  84. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }


    }

    View Slide

  85. Mas ao rodar…

    View Slide

  86. View Slide

  87. Hein?

    View Slide

  88. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }


    }

    View Slide

  89. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }


    }
    - carregando apenas 50 objetos por vez

    View Slide

  90. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }


    }
    - criando nova referência
    - carregando apenas 50 objetos por vez

    View Slide

  91. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }


    }
    - criando nova referência
    - carregando apenas 50 objetos por vez
    - saindo do loop na hora certa

    View Slide

  92. Por que?

    View Slide

  93. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }


    }

    View Slide

  94. @Repository


    public interface ProposalRepository extends JpaRepository {


    public List findAllByStatusOrderByCreatedAtAsc(ProposalStatus status);


    }

    View Slide

  95. Revisitando JPA
    e Hibernate

    View Slide

  96. Job

    View Slide

  97. Repository
    Job

    View Slide

  98. Repository EM
    Job

    View Slide

  99. Repository EM
    Job

    View Slide

  100. Repository EM
    Job

    View Slide

  101. Repository EM
    Job

    View Slide

  102. Repository EM
    Job

    View Slide

  103. Repository EM
    Job

    View Slide

  104. Repository EM
    Job

    View Slide

  105. Long-running transaction

    View Slide

  106. @Transactional
    Repository EM
    Job
    Long-running transaction

    View Slide

  107. @Transactional
    Repository EM
    Job
    Long-running transaction

    View Slide

  108. @Transactional
    Repository EM
    Job
    Long-running transaction

    View Slide

  109. @Transactional
    Repository EM
    Job
    (First-Level Cache)
    Long-running transaction

    View Slide

  110. @Transactional
    Repository EM
    Job
    (First-Level Cache)
    Long-running transaction

    View Slide

  111. A sacada aqui é entender…

    View Slide

  112. @Transactional
    Repository EM
    Job
    (First-Level Cache)
    Long-running transaction

    View Slide

  113. @Transactional
    EM
    (First-Level Cache)
    Long-running transaction

    View Slide

  114. @Transactional
    EM
    (First-Level Cache)
    Long-running transaction

    View Slide

  115. EM
    (First-Level Cache)
    @Transactional
    Long-running transaction

    View Slide

  116. EM
    (First-Level Cache)
    @Transactional
    Long-running transaction

    View Slide

  117. Repository EM
    Job
    (First-Level Cache)
    @Transactional
    Long-running transaction

    View Slide

  118. Repository EM
    Job
    (First-Level Cache)
    Long-running transaction

    View Slide

  119. Ahhh!!

    View Slide

  120. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Transactional


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }


    }

    View Slide

  121. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }


    }
    inicia e termina
    uma transação

    View Slide

  122. Mas…

    View Slide

  123. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    }


    }


    }

    View Slide

  124. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard); // inicia e comita transação


    proposal.attachTo(newCard);


    proposalRepository.save(proposal); // inicia e comita transação


    });


    }


    }


    }
    analogo a

    auto_commit = true

    View Slide

  125. @Component


    public class AttachCardsToProposalsJob {


    // dependências e constantes


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(ELIGIBLE);


    if (proposals.isEmpty()) {


    break;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard); // inicia e comita transação


    proposal.attachTo(newCard);


    proposalRepository.save(proposal); // inicia e comita transação


    });


    }


    }


    }
    Abrimos mão da
    CONSISTÊNCIA dos dados!

    View Slide

  126. @Component


    View Slide

  127. Short-running transaction

    View Slide

  128. Job
    Transaction
    Job

    View Slide

  129. Job
    Transaction
    Job
    inicio do job
    fi
    m do job

    View Slide

  130. Job
    Transaction
    Job

    View Slide

  131. inicio da
    transação
    fi
    m da
    transação
    Job
    Job
    Transaction

    View Slide

  132. Job
    Transaction
    Job
    Long-running transaction

    View Slide

  133. Job
    Transaction
    Job

    View Slide

  134. Job
    Transaction
    Job

    View Slide

  135. Job
    Transaction
    Job
    inicio da
    transação
    fi
    m da
    transação

    View Slide

  136. Job
    Transaction
    Job
    Short-running transaction

    View Slide

  137. Job
    Transaction
    Job

    View Slide

  138. Job
    Transaction
    Job

    View Slide

  139. Job
    Transaction
    Job

    View Slide

  140. Job
    Transaction
    Job

    View Slide

  141. Job
    Transaction
    Job

    View Slide

  142. Job
    Transaction
    Job
    Short-running transaction

    View Slide

  143. Repository EM
    Job
    (First-Level Cache)
    Short-running transaction

    View Slide

  144. Repository EM
    Job
    @Transactional
    Short-running transaction
    (First-Level Cache)

    View Slide

  145. Repository EM
    Job
    @Transactional
    Short-running transaction
    (First-Level Cache)

    View Slide

  146. Repository EM
    Job
    @Transactional
    Short-running transaction
    (First-Level Cache)

    View Slide

  147. @Transactional
    Repository EM
    Job
    (First-Level Cache)
    Short-running transaction

    View Slide

  148. Repository EM
    Job
    @Transactional
    Short-running transaction
    (First-Level Cache)

    View Slide

  149. Controle transacional programático

    View Slide

  150. @Component


    public class AttachCardsToProposalsJob {




    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {




    // executa código em escopo transacional




    }


    }


    }

    View Slide

  151. @Component


    public class AttachCardsToProposalsJob {




    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    @Transactional.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide

  152. @Component


    public class AttachCardsToProposalsJob {




    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    @Transactional.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide

  153. @Component


    public class AttachCardsToProposalsJob {




    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    @Transactional.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide

  154. @Component


    public class AttachCardsToProposalsJob {




    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    @Transactional.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide

  155. @Component


    public class AttachCardsToProposalsJob {




    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    @Transactional.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide

  156. @Component


    public class AttachCardsToProposalsJob {




    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    @Transactional.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide

  157. @Component


    public class AttachCardsToProposalsJob {




    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    @Transactional.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide

  158. @Component


    public class AttachCardsToProposalsJob {




    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    transactionManager.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide

  159. @Component


    public class AttachCardsToProposalsJob {


    private TransactionTemplate transactionManager;


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    transactionManager.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide

  160. @Component


    public class AttachCardsToProposalsJob {


    private TransactionTemplate transactionManager;


    @Scheduled(fixedDelay = 60_000)


    public void execute() {


    while (true) {


    transactionManager.execute(transaction -> {


    // executa código em escopo transacional


    });


    }


    }


    }

    View Slide



  161. @Autowired


    private TransactionTemplate transactionManager;


    @Scheduled(fixedDelay = 60_000)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(transactionStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }


    View Slide



  162. @Autowired


    private TransactionTemplate transactionManager;


    @Scheduled(fixedDelay = 60_000)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(transactionStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }


    View Slide

  163. View Slide

  164. inicio do job
    fi
    m do job

    View Slide

  165. E SE…

    View Slide

  166. …rodarmos a
    aplicação em
    cluster?

    View Slide

  167. View Slide

  168. View Slide

  169. View Slide

  170. View Slide

  171. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  172. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  173. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  174. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  175. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  176. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  177. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  178. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE_WITH_ATTACHED_CARD
    1004 ELIGIBLE

    View Slide

  179. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE_WITH_ATTACHED_CARD
    1004 ELIGIBLE

    View Slide

  180. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE_WITH_ATTACHED_CARD
    1004 ELIGIBLE_WITH_ATTACHED_CARD

    View Slide

  181. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE_WITH_ATTACHED_CARD
    1004 ELIGIBLE_WITH_ATTACHED_CARD

    View Slide

  182. View Slide

  183. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  184. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  185. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  186. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE
    2x

    View Slide

  187. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    1004 ELIGIBLE
    2x

    View Slide

  188. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    1004 ELIGIBLE
    2x
    2x

    View Slide

  189. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE_WITH_ATTACHED_CARD
    1004 ELIGIBLE
    2x
    2x

    View Slide

  190. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE_WITH_ATTACHED_CARD
    1004 ELIGIBLE
    2x
    2x
    2x

    View Slide

  191. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE_WITH_ATTACHED_CARD
    1004 ELIGIBLE_WITH_ATTACHED_CARD
    2x
    2x
    2x

    View Slide

  192. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE_WITH_ATTACHED_CARD
    1004 ELIGIBLE_WITH_ATTACHED_CARD
    2x
    2x
    2x
    2x

    View Slide

  193. Oops!

    View Slide

  194. Synchronized?

    View Slide

  195. @Component


    public class AttachCardsToProposalsJob {


    @Scheduled(...)


    public synchronized void execute() {


    // executa lógica aqui


    }


    }

    View Slide

  196. View Slide

  197. View Slide

  198. View Slide

  199. View Slide

  200. @Component


    View Slide

  201. Distributed Lock?

    View Slide

  202. @Scheduled(...)


    public void execute() {


    if (isLeader()) {


    // executa lógica aqui


    }


    }

    View Slide

  203. @Scheduled(...)


    public void execute() {


    if (isLeader()) {


    // executa lógica aqui


    }


    }

    View Slide

  204. View Slide

  205. View Slide

  206. View Slide

  207. View Slide

  208. View Slide

  209. View Slide

  210. View Slide

  211. View Slide

  212. View Slide

  213. View Slide

  214. View Slide

  215. Não!

    View Slide

  216. Que tal…

    View Slide

  217. View Slide

  218. View Slide

  219. @Repository


    public interface ProposalRepository extends JpaRepository {




    public List findTop50ByStatusOrderByCreatedAtAsc(ProposalStatus status);


    }

    View Slide

  220. @Repository


    public interface ProposalRepository extends JpaRepository {


    @Lock(LockModeType.PESSIMISTIC_WRITE)


    public List findTop50ByStatusOrderByCreatedAtAsc(ProposalStatus status);


    }

    View Slide

  221. select p.*


    from proposal p


    where p.status = 'ELIGIBLE'


    order by p.created_at asc


    limit 50

    View Slide

  222. select p.*


    from proposal p


    where p.status = 'ELIGIBLE'


    order by p.created_at asc


    limit 50

    for update

    View Slide

  223. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  224. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  225. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  226. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  227. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  228. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  229. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  230. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  231. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  232. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  233. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  234. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  235. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  236. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  237. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE_WITH_ATTACHED_CARD
    ... ...

    View Slide

  238. SIM!

    View Slide

  239. E SE…

    View Slide

  240. …precisarmos
    processar em
    paralelo?

    View Slide

  241. View Slide

  242. View Slide

  243. View Slide

  244. View Slide

  245. Para isso…

    View Slide

  246. 9630 9631
    QUEUE (
    fi
    la)
    9629

    View Slide

  247. 9630 9631
    QUEUE (
    fi
    la)
    producer
    9629
    produz:

    1000 itens/min

    View Slide

  248. 9630 9631
    QUEUE (
    fi
    la)
    producer
    consumer
    9629
    produz:

    1000 itens/min consume:

    600 itens/min

    View Slide

  249. 9630 9631
    QUEUE (
    fi
    la)
    producer
    consumer
    9629
    produz:

    1000 itens/min
    consumer
    consume:

    400 itens/min
    consume:

    600 itens/min

    View Slide

  250. 9630 9631
    QUEUE (
    fi
    la)
    producer
    consumer
    9629
    produz:

    1000 itens/min
    consumer
    consume:

    400 itens/min
    consume:

    600 itens/min

    View Slide

  251. View Slide

  252. Não!

    View Slide

  253. Que tal…

    View Slide

  254. View Slide

  255. @Repository


    public interface ProposalRepository extends JpaRepository {


    @QueryHints({


    @QueryHint(


    name = "javax.persistence.lock.timeout",


    value = LockOptions.SKIP_LOCKED) // org.hibernate.LockOptions


    })


    @Lock(LockModeType.PESSIMISTIC_WRITE)


    public List findTop50ByStatusOrderByCreatedAtAsc(ProposalStatus status);


    }

    View Slide

  256. @Repository


    public interface ProposalRepository extends JpaRepository {


    @QueryHints({


    @QueryHint(


    name = "javax.persistence.lock.timeout",


    value = LockOptions.SKIP_LOCKED) // org.hibernate.LockOptions


    })


    @Lock(LockModeType.PESSIMISTIC_WRITE)


    public List findTop50ByStatusOrderByCreatedAtAsc(ProposalStatus status);


    }

    View Slide

  257. select p.*


    from proposal p


    where p.status = 'ELIGIBLE'


    order by p.created_at asc


    limit 50

    for update

    View Slide

  258. select p.*


    from proposal p


    where p.status = 'ELIGIBLE'


    order by p.created_at asc


    limit 50

    for update skip locked

    View Slide

  259. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  260. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  261. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  262. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  263. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  264. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  265. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    ... ...

    View Slide

  266. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  267. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  268. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  269. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  270. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  271. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  272. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  273. ID STATUS
    1001 ELIGIBLE_WITH_ATTACHED_CARD
    1002 ELIGIBLE_WITH_ATTACHED_CARD
    1003 ELIGIBLE
    ... ...

    View Slide

  274. Ou seja…

    View Slide

  275. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  276. ID STATUS
    1001 ELIGIBLE
    1002 ELIGIBLE
    1003 ELIGIBLE
    1004 ELIGIBLE

    View Slide

  277. SIM!

    View Slide

  278. E SE…

    View Slide

  279. …impactar no
    startup da
    aplicação?

    View Slide

  280. @Scheduled(


    fixedDelay = 60_000


    )


    public void execute() {


    // executa lógica aqui


    }

    View Slide

  281. initialDelay

    View Slide

  282. @Scheduled(


    fixedDelay = 60_000,


    initialDelay = 120_000


    )


    public void execute() {


    // executa lógica aqui


    }

    View Slide

  283. E SE…

    View Slide

  284. …precisarmos
    de maior
    throughput?

    View Slide

  285. Hibernate batch size

    View Slide



  286. @Autowired


    private TransactionTemplate transactionManager;


    @Scheduled(fixedDelay = 60_000)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(transactionStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }


    View Slide



  287. @Autowired


    private TransactionTemplate transactionManager;


    @Scheduled(fixedDelay = 60_000)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(transactionStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }


    View Slide



  288. @Autowired


    private TransactionTemplate transactionManager;


    @Scheduled(fixedDelay = 60_000)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(transactionStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }


    View Slide



  289. @Autowired


    private TransactionTemplate transactionManager;


    @Scheduled(fixedDelay = 60_000)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(transactionStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }


    View Slide



  290. @Autowired


    private TransactionTemplate transactionManager;


    @Scheduled(fixedDelay = 60_000)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(transactionStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }


    Insere novo
    cartão
    Atualiza
    proposta com
    cartão

    View Slide

  291. Para cada iteração do loop…

    View Slide

  292. 2 roundtrips
    INSERT INTO card(cardNumber) VALUES ('5260-9991-9040-0001');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2021;

    View Slide

  293. 2 roundtrips
    INSERT INTO card(cardNumber) VALUES ('5260-9991-9040-0001');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2021;

    View Slide

  294. 2 roundtrips
    INSERT INTO card(cardNumber) VALUES ('5260-9991-9040-0001');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2021;

    View Slide

  295. INSERT INTO card(cardNumber) VALUES ('5260-9991-9040-0001');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2021;
    INSERT INTO card(cardNumber) VALUES (' 5513-8141-9261-0002');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2022;
    4 roundtrips

    View Slide

  296. INSERT INTO card(cardNumber) VALUES ('5260-9991-9040-0001');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2021;
    INSERT INTO card(cardNumber) VALUES (' 5513-8141-9261-0002');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2022;
    INSERT INTO card(cardNumber) VALUES (' 5599-4154-6354-0003');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2023;
    6 roundtrips

    View Slide

  297. INSERT INTO card(cardNumber) VALUES ('5260-9991-9040-0001');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2021;
    INSERT INTO card(cardNumber) VALUES (' 5513-8141-9261-0002');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2022;
    INSERT INTO card(cardNumber) VALUES (' 5599-4154-6354-0003');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2023;
    INSERT INTO card(cardNumber) VALUES (' 5419-8753-1126-0004');
    UPDATE proposal SET status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2024;
    INSERT INTO ...
    UPDATE proposal SET ...
    100 roundtrips

    View Slide

  298. Latência

    View Slide

  299. spring:


    jpa:


    properties:


    hibernate:


    jdbc:


    batch_size: 50


    order_inserts: true


    order_updates: true
    application.yml

    View Slide

  300. spring:


    jpa:


    properties:


    hibernate:


    jdbc:


    batch_size: 50


    order_inserts: true


    order_updates: true
    application.yml

    View Slide

  301. 2 roundtrips
    INSERT INTO card(cardNumber) VALUES (?)
    Params: [

    ('5513-8141-9261-0001'),

    ('5513-8141-9261-0002'),

    (‘5513-8141-9261-0003'),


    (‘5513-8141-9261-0004'),


    ...

    ]
    UPDATE proposal SET status = ? WHERE id = ?;
    Params: [

    (‘ELIGIBLE_WITH_ATTACHED_CARD’, 2021),

    ('ELIGIBLE_WITH_ATTACHED_CARD', 2022),

    ('ELIGIBLE_WITH_ATTACHED_CARD', 2023),


    ('ELIGIBLE_WITH_ATTACHED_CARD', 2024),


    ...

    ]

    View Slide

  302. 2 roundtrips
    INSERT INTO card(cardNumber) VALUES (?)
    Params: [

    ('5513-8141-9261-0001'),

    ('5513-8141-9261-0002'),

    (‘5513-8141-9261-0003'),


    (‘5513-8141-9261-0004'),


    ...

    ]
    UPDATE proposal SET status = ? WHERE id = ?;
    Params: [

    (‘ELIGIBLE_WITH_ATTACHED_CARD’, 2021),

    ('ELIGIBLE_WITH_ATTACHED_CARD', 2022),

    ('ELIGIBLE_WITH_ATTACHED_CARD', 2023),


    ('ELIGIBLE_WITH_ATTACHED_CARD', 2024),


    ...

    ]

    View Slide

  303. INSERT INTO card(cardNumber) VALUES (?)
    Params: [

    ('5513-8141-9261-0001'),

    ('5513-8141-9261-0002'),

    (‘5513-8141-9261-0003'),


    (‘5513-8141-9261-0004'),


    ...

    ]
    2 roundtrips
    UPDATE proposal SET status = ? WHERE id = ?;
    Params: [

    (‘ELIGIBLE_WITH_ATTACHED_CARD’, 2021),

    ('ELIGIBLE_WITH_ATTACHED_CARD', 2022),

    ('ELIGIBLE_WITH_ATTACHED_CARD', 2023),


    ('ELIGIBLE_WITH_ATTACHED_CARD', 2024),


    ...

    ]

    View Slide

  304. E SE…

    View Slide

  305. …ocorrer erro
    na integração?

    View Slide

  306. @Scheduled(...)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(txStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }

    View Slide

  307. @Scheduled(...)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(txStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }

    View Slide

  308. @Scheduled(...)


    public void execute() {




    boolean pending = true;


    while (pending) {


    pending = transactionManager.execute(txStatus -> {


    List proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…);


    if (proposals.isEmpty()) {


    return false;


    }


    proposals.forEach(proposal -> {


    CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());


    Card newCard = cardData.toModel();


    cardRepository.save(newCard);


    proposal.attachTo(newCard);


    proposalRepository.save(proposal);


    });


    return true;


    });


    }


    }

    View Slide

  309. https://deniseyu.io/art/

    View Slide

  310. CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());

    View Slide

  311. CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());
    espera in
    fi
    nita? timeout

    View Slide

  312. CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());
    espera in
    fi
    nita? timeout
    erros intermitentes? retry

    View Slide

  313. CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId());
    espera in
    fi
    nita? timeout
    erros intermitentes? retry
    posso reenviar a
    requisição? idempotência

    View Slide

  314. E SE…
    E SE…
    E SE…
    E SE… E SE…
    E SE…
    E SE… E SE…
    E SE…
    E SE…
    E SE…
    E SE
    E SE…
    SE… E SE…
    E SE…
    E SE…
    E SE…
    E SE…
    E SE…
    SE…
    E SE…

    View Slide

  315. CONCLUINDO

    View Slide

  316. @rponte
    Rafael Ponte

    View Slide