Почему Amazon sqs не поддерживает транзакции?

Весной Rabbitmq поддерживает транзакции. Однако Amazon sqs не поддерживает транзакции в Spring.

Мне жаль. Я добавил еще немного контента. Я протестировал две очереди сообщений (rabbitmq, amazon sqs), как показано ниже весной.

Моя цель - логика обработки электронного письма пользователя в очередь для отправки электронного письма о завершении регистрации, когда пользователь завершает регистрацию без исключения.

//rabbit mq configuration.class
@Bean
public ConnectionFactory rabbitConnectionFactory() {
    CachingConnectionFactory connectionFactory =
            new CachingConnectionFactory("localhost",5672);
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    return connectionFactory;
}

@Bean
public SimpleMessageListenerContainer messageListenerContainer() {
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(rabbitConnectionFactory());
    container.setQueueNames(queue);
   container.setMessageListener(exampleListener());
    container.setTransactionManager(platformTransactionManager);
    container.setChannelTransacted(true);
    return container;
}


@Bean
public RabbitTemplate producerRabbitTemplate() {
    RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
    rabbitTemplate.setQueue(queue);
    rabbitTemplate.setMandatory(true);
    rabbitTemplate.isChannelTransacted();
    rabbitTemplate.setChannelTransacted(true);
    return rabbitTemplate;
}



//UserService.class
@Autowired
private final UserRepository userRepository;
@Autowired
private final RabbitTemplate rabbitTemplate;

@Transactional
public User createUser(final User user){
    rabbitTemplate.convertAndSend("spring-boot", user.getEmail()); // SignUp Completion email
    final User user = userRepository.save(user);
    if(true) throw new RuntimeException();
    return user;
}

Тем не менее, логика выше возникает исключение runtimeException.

Rabbit mq не будет отправлять данные в очередь из-за транзакционной аннотации в Spring, если возникает исключение.

//amazon sqs configuration.class
@Bean
public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSqs) {

//UserService.class
@Autowired
private final UserRepository userRepository;
@Autowired
private QueueMessagingTemplate messagingTemplate;


@Transactional
public User createUser(final User user){
    messagingTemplate.convertAndSend("spring-boot", user.getEmail()); // SignUp Completion email
    final User user = userRepository.save(user);
    if(true) throw new RuntimeException();
    return user;
}

Однако sqs отправит данные в очередь, даже если произойдет исключение.

Кто-нибудь знает почему это?

2 ответа

Решение

TLDR Как я могу решить эту проблему?

Не пытайтесь использовать транзакции для этого, придумайте какой-нибудь способ сделать систему в конечном итоге непротиворечивой.


Звучит так, будто вы хотите выполнить "queue.sendMessage" и "repository.save", как если бы они были транзакцией - либо обе были зафиксированы, либо ни одна не была зафиксирована. Проблема в том, что "транзакция" на самом деле не транзакционная, даже в вашем примере с кроликом.

Рассмотрим основы работы транзакции:

  1. что-то подобное begin шаг, который означает, что следующие изменения являются частью транзакции
  2. изменения сделаны (но не видны читателям)
  3. что-то подобное commit шаг для атомарной фиксации изменений (делая их видимыми для читателей)

Однако в вашем случае очередь и хранилище - это отдельные объекты, поддерживаемые отдельными сетевыми ресурсами, которые не общаются друг с другом. В этом случае нет атомарного коммита. Без атомарного коммита вы не сможете получить истинную транзакцию. Это "работает" в вашей демонстрации, потому что исключение отделено от кода, который выполняет запись.

Рассмотрим этот случай, чтобы более наглядно проиллюстрировать:

@Transactional
public User createUser(final User user){
    messagingTemplate.convertAndSend("spring-boot", user.getEmail());
    final User user = userRepository.save(user);
    return user;
}
  • Когда для этого начинается шаг фиксации, он должен сделать видимым и сообщение (в очереди), и пользователя (в репо)
  • Для этого необходимо выполнить сетевой вызов как в очередь, так и в репозиторий.
    • тот факт, что эти два вызова являются источником проблемы
  • Если один из них удастся, а другой не получится, вы окажетесь в противоречивом состоянии
    • Вы можете сказать "транзакции можно откатить", но откат может привести к другому сетевому вызову, который, конечно, может дать сбой.

Есть способы сделать всю распределенную систему транзакционной, но это очень сложная проблема. Часто гораздо проще и быстрее учесть временную несогласованность и создать механизмы, позволяющие в конечном итоге привести систему в соответствие.

Шаблон транзакционных исходящих сообщений также является решением для достижения конечной согласованности. Подробности здесь: https://microservices.io/patterns/data/transactional-outbox.html .

Другие вопросы по тегам