Share
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
Transcript
316 slides
-
DISTRIBUTED SCHEDULING | Spring Boot os desa fi os e
percalços de implementar um job em background -
@Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void
runQuiteOften() { // lógica do job vai aqui } } -
@Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void
runQuiteOften() { // lógica do job vai aqui } } -
@Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void
runQuiteOften() { // lógica do job vai aqui } } -
@Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void
runQuiteOften() { // lógica do job vai aqui } } -
@Component public class OneJob { public void runQuiteOften() { //
lógica do job vai aqui } } -
@Component public class OneJob { @Scheduled public void runQuiteOften() {
// lógica do job vai aqui } } -
@Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void
runQuiteOften() { // lógica do job vai aqui } } -
@Component public class OneJob { @Scheduled(fixedDelay = 60_000) public void
runQuiteOften() { // lógica do job vai aqui } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
DISTRIBUTED SCHEDULING | Spring Boot os desa fi os e
percalços de implementar um job em background -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
select p.* from proposal p where p.status = 'ELIGIBLE' order
by p.created_at asc -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Repository public interface ProposalRepository extends JpaRepository<Proposal, UUID> { public List<Proposal>
findAllByStatusOrderByCreatedAtAsc(ProposalStatus status); } -
@Repository public interface ProposalRepository extends JpaRepository<Proposal, UUID> { public List<Proposal>
findAllByStatusOrderByCreatedAtAsc(ProposalStatus status); } -
@Repository public interface ProposalRepository extends JpaRepository<Proposal, UUID> { public List<Proposal>
findAllByStatusOrderByCreatedAtAsc(ProposalStatus status); } -
select p.* from proposal p where p.status = 'ELIGIBLE' order
by p.created_at asc -
select p.* from proposal p where p.status = 'ELIGIBLE' order
by p.created_at asc -
select p.* from proposal p where p.status = 'ELIGIBLE' order
by p.created_at asc limit 50 -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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); }); } } -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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 ! -
@Component public class AttachCardsToProposalsJob { private CardsClient cardsClient; private CardRepository
cardRepository; private ProposalRepository proposalRepository; @Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> 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! -
@Transactional @Scheduled(fixedDelay = 60_000) public void execute() { List<Proposal> proposals
= proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…); proposals.forEach(proposal -> { // processa cada proposta }); } -
@Transactional @Scheduled(fixedDelay = 60_000) public void execute() { while (true)
{ List<Proposal> proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…); if (proposals.isEmpty()) { break; } proposals.forEach(proposal -> { // processa cada proposta }); } } -
@Transactional @Scheduled(fixedDelay = 60_000) public void execute() { while (true)
{ List<Proposal> proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…); if (proposals.isEmpty()) { break; } proposals.forEach(proposal -> { // processa cada proposta }); } } -
@Transactional @Scheduled(fixedDelay = 60_000) public void execute() { while (true)
{ List<Proposal> proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…); if (proposals.isEmpty()) { break; } proposals.forEach(proposal -> { // processa cada proposta }); } } -
@Transactional @Scheduled(fixedDelay = 60_000) public void execute() { while (true)
{ List<Proposal> proposals = proposalRepository.findTop50ByStatusOrderByCreatedAtAsc(…); if (proposals.isEmpty()) { break; } proposals.forEach(proposal -> { // processa cada proposta }); } } -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Transactional
@Scheduled(fixedDelay = 60_000) public void execute() { while (true) { List<Proposal> 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); }); } } } -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Transactional
@Scheduled(fixedDelay = 60_000) public void execute() { while (true) { List<Proposal> 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); }); } } } -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Transactional
@Scheduled(fixedDelay = 60_000) public void execute() { while (true) { List<Proposal> 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 -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Transactional
@Scheduled(fixedDelay = 60_000) public void execute() { while (true) { List<Proposal> 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 -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Transactional
@Scheduled(fixedDelay = 60_000) public void execute() { while (true) { List<Proposal> 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 -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Transactional
@Scheduled(fixedDelay = 60_000) public void execute() { while (true) { List<Proposal> 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); }); } } } -
@Repository public interface ProposalRepository extends JpaRepository<Proposal, UUID> { public List<Proposal>
findAllByStatusOrderByCreatedAtAsc(ProposalStatus status); } -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Transactional
@Scheduled(fixedDelay = 60_000) public void execute() { while (true) { List<Proposal> 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); }); } } } -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Scheduled(fixedDelay
= 60_000) public void execute() { while (true) { List<Proposal> 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 -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Scheduled(fixedDelay
= 60_000) public void execute() { while (true) { List<Proposal> 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); }); } } } -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Scheduled(fixedDelay
= 60_000) public void execute() { while (true) { List<Proposal> 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 -
@Component public class AttachCardsToProposalsJob { // dependências e constantes @Scheduled(fixedDelay
= 60_000) public void execute() { while (true) { List<Proposal> 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! -
@Component public class AttachCardsToProposalsJob { @Scheduled(fixedDelay = 60_000) public void
execute() { while (true) { // executa código em escopo transacional } } } -
@Component public class AttachCardsToProposalsJob { @Scheduled(fixedDelay = 60_000) public void
execute() { while (true) { @Transactional.execute(transaction -> { // executa código em escopo transacional }); } } } -
@Component public class AttachCardsToProposalsJob { @Scheduled(fixedDelay = 60_000) public void
execute() { while (true) { @Transactional.execute(transaction -> { // executa código em escopo transacional }); } } } -
@Component public class AttachCardsToProposalsJob { @Scheduled(fixedDelay = 60_000) public void
execute() { while (true) { @Transactional.execute(transaction -> { // executa código em escopo transacional }); } } } -
@Component public class AttachCardsToProposalsJob { @Scheduled(fixedDelay = 60_000) public void
execute() { while (true) { @Transactional.execute(transaction -> { // executa código em escopo transacional }); } } } -
@Component public class AttachCardsToProposalsJob { @Scheduled(fixedDelay = 60_000) public void
execute() { while (true) { @Transactional.execute(transaction -> { // executa código em escopo transacional }); } } } -
@Component public class AttachCardsToProposalsJob { @Scheduled(fixedDelay = 60_000) public void
execute() { while (true) { @Transactional.execute(transaction -> { // executa código em escopo transacional }); } } } -
@Component public class AttachCardsToProposalsJob { @Scheduled(fixedDelay = 60_000) public void
execute() { while (true) { @Transactional.execute(transaction -> { // executa código em escopo transacional }); } } } -
@Component public class AttachCardsToProposalsJob { @Scheduled(fixedDelay = 60_000) public void
execute() { while (true) { transactionManager.execute(transaction -> { // executa código em escopo transacional }); } } } -
@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 }); } } } -
@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 }); } } } -
@Autowired private TransactionTemplate transactionManager; @Scheduled(fixedDelay = 60_000) public void execute()
{ boolean pending = true; while (pending) { pending = transactionManager.execute(transactionStatus -> { List<Proposal> 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; }); } } -
@Autowired private TransactionTemplate transactionManager; @Scheduled(fixedDelay = 60_000) public void execute()
{ boolean pending = true; while (pending) { pending = transactionManager.execute(transactionStatus -> { List<Proposal> 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; }); } } -
@Component public class AttachCardsToProposalsJob { @Scheduled(...) public synchronized void execute()
{ // executa lógica aqui } } -
@Repository public interface ProposalRepository extends JpaRepository<Proposal, UUID> { public List<Proposal>
findTop50ByStatusOrderByCreatedAtAsc(ProposalStatus status); } -
@Repository public interface ProposalRepository extends JpaRepository<Proposal, UUID> { @Lock(LockModeType.PESSIMISTIC_WRITE) public
List<Proposal> findTop50ByStatusOrderByCreatedAtAsc(ProposalStatus status); } -
select p.* from proposal p where p.status = 'ELIGIBLE' order
by p.created_at asc limit 50 -
select p.* from proposal p where p.status = 'ELIGIBLE' order
by p.created_at asc limit 50 for update -
9630 9631 QUEUE ( fi la) producer 9629 produz:
1000 itens/min -
9630 9631 QUEUE ( fi la) producer consumer 9629 produz:
1000 itens/min consume: 600 itens/min -
9630 9631 QUEUE ( fi la) producer consumer 9629 produz:
1000 itens/min consumer consume: 400 itens/min consume: 600 itens/min -
9630 9631 QUEUE ( fi la) producer consumer 9629 produz:
1000 itens/min consumer consume: 400 itens/min consume: 600 itens/min -
@Repository public interface ProposalRepository extends JpaRepository<Proposal, UUID> { @QueryHints({ @QueryHint(
name = "javax.persistence.lock.timeout", value = LockOptions.SKIP_LOCKED) // org.hibernate.LockOptions }) @Lock(LockModeType.PESSIMISTIC_WRITE) public List<Proposal> findTop50ByStatusOrderByCreatedAtAsc(ProposalStatus status); } -
@Repository public interface ProposalRepository extends JpaRepository<Proposal, UUID> { @QueryHints({ @QueryHint(
name = "javax.persistence.lock.timeout", value = LockOptions.SKIP_LOCKED) // org.hibernate.LockOptions }) @Lock(LockModeType.PESSIMISTIC_WRITE) public List<Proposal> findTop50ByStatusOrderByCreatedAtAsc(ProposalStatus status); } -
select p.* from proposal p where p.status = 'ELIGIBLE' order
by p.created_at asc limit 50 for update -
select p.* from proposal p where p.status = 'ELIGIBLE' order
by p.created_at asc limit 50 for update skip locked -
@Scheduled( fixedDelay = 60_000 ) public void execute() { //
executa lógica aqui } -
@Scheduled( fixedDelay = 60_000, initialDelay = 120_000 ) public void
execute() { // executa lógica aqui } -
@Autowired private TransactionTemplate transactionManager; @Scheduled(fixedDelay = 60_000) public void execute()
{ boolean pending = true; while (pending) { pending = transactionManager.execute(transactionStatus -> { List<Proposal> 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; }); } } -
@Autowired private TransactionTemplate transactionManager; @Scheduled(fixedDelay = 60_000) public void execute()
{ boolean pending = true; while (pending) { pending = transactionManager.execute(transactionStatus -> { List<Proposal> 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; }); } } -
@Autowired private TransactionTemplate transactionManager; @Scheduled(fixedDelay = 60_000) public void execute()
{ boolean pending = true; while (pending) { pending = transactionManager.execute(transactionStatus -> { List<Proposal> 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; }); } } -
@Autowired private TransactionTemplate transactionManager; @Scheduled(fixedDelay = 60_000) public void execute()
{ boolean pending = true; while (pending) { pending = transactionManager.execute(transactionStatus -> { List<Proposal> 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; }); } } -
@Autowired private TransactionTemplate transactionManager; @Scheduled(fixedDelay = 60_000) public void execute()
{ boolean pending = true; while (pending) { pending = transactionManager.execute(transactionStatus -> { List<Proposal> 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 -
2 roundtrips INSERT INTO card(cardNumber) VALUES ('5260-9991-9040-0001'); UPDATE proposal SET
status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2021; -
2 roundtrips INSERT INTO card(cardNumber) VALUES ('5260-9991-9040-0001'); UPDATE proposal SET
status = 'ELIGIBLE_WITH_ATTACHED_CARD' WHERE id = 2021; -
2 roundtrips 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 ('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 -
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 -
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 -
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), ... ] -
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), ... ] -
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), ... ] -
@Scheduled(...) public void execute() { boolean pending = true; while
(pending) { pending = transactionManager.execute(txStatus -> { List<Proposal> 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; }); } } -
@Scheduled(...) public void execute() { boolean pending = true; while
(pending) { pending = transactionManager.execute(txStatus -> { List<Proposal> 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; }); } } -
@Scheduled(...) public void execute() { boolean pending = true; while
(pending) { pending = transactionManager.execute(txStatus -> { List<Proposal> 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; }); } } -
CardDataResponse cardData = cardsClient.findCardByProposalId(proposal.getId()); espera in fi nita? timeout erros
intermitentes? retry posso reenviar a requisição? idempotência -
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…