Почему 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", как если бы они были транзакцией - либо обе были зафиксированы, либо ни одна не была зафиксирована. Проблема в том, что "транзакция" на самом деле не транзакционная, даже в вашем примере с кроликом.
Рассмотрим основы работы транзакции:
- что-то подобное
begin
шаг, который означает, что следующие изменения являются частью транзакции - изменения сделаны (но не видны читателям)
- что-то подобное
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 .