Assume some spring bean has transactional method and it does two things:
asynchronously send JMS messages
updates DB
The problem is that when the message is consumed, the DB transaction sometimes not have committed, and handler failed to find in DB expected value.
The evident solution - is to send in transaction, but I use async send available on ActiveMQ to increase throughput. If I make send transactional, I forfeit asynchrocity and also risk getting OuOfMemory.
How would you solve this problem?
You need to use an XA-enabled transaction manager, and send the JMS message and do the update to the DB in the same, distributed transaction.
This doesn't change anything to asynchronicity: the send call will return before the receiver receives the message. But when it receives it, the transaction will be committed, and the receiver will find the updates in the database.
I don't see what OutOfMemoryErrors have to do with this.
Related
I have REST API gateway which calls one of the microservices with MassTransit request client. This request is not durable and is meant to live for a short time - essentially it's just replacement of "traditional" synchronous (via HTTP/GRPC/etc) gateway-microservice communication.
On microservice side I have consumer which under the hood uses DbContext and Transaction (EFC) to perform some work in database. After the work is done it should publish "WorkDoneEvent" (to be consumed later by other microservices) and return result of the work to api gateway. Event must be published atomically along with transaction used to perform the work. It does not matter if ApiGateway will receive response / will retry request - as soon as transaction is commited both work result and sending "WorkDoneEvent" must be guaranteed.
Normally this is done with transactional outbox which first saves published event to database within same transaction as the work is done. (And then some process constantly "polls" outbox and tries send message to the broker, when done it removes message from outbox). As far as I know.
MassTransit seems to have transactional outbox built in: https://masstransit-project.com/advanced/middleware/transactions.html#transactional-bus.
However in docs it clearly states:
Never use the TransactionalBus or TransactionalEnlistmentBus when writing consumers. These tools are very specific and should be used only in the scenarios described.
And this is exactly what I want to do...
Why I should not do it?
I'd suggest using the InMemoryOutbox, which is part of MassTransit. It's significantly lighter weight, is designed to work in a consumer, and will not publish your events until after the consumer has completed (but prior to acknowledging the message at the broker). The only consideration is that your consumer should be idempotent (which needs to be the case in your approach as well) and if the operation was already performed on a retry, it should republish the events.
There are videos, articles, and a sample to go along with it.
What does JMS session exactly mean?
What does it mean that a JMS session can be or not "transacted"?
What does it mean that a JMS session can be or not with "auto-acknowledge"?
You could view a JMS Session as the link between a connection and a thread's work on that connection. A JMS Session is a 'single-threaded context', so each thread must use a different session. In contrast, a JMS connection can be shared between multiple threads. Also, one thread could own multiple sessions.
The send/receive operations on a Session are either acknowledged or transacted.
Imagine a consumer crash: usually you want to redeliver a message if the consumer said it hasn't processed a message yet. So an acknowledgment (ACK) can be send from consumer to Broker to tell the JMS Broker that the message has been processed. Not just received but also stored in the Database, or whatever work the consumer is doing. If the consumer is restarted after the crash it will receive all messages that have not yet been acknowledged.
There are different forms of ACKs: auto-acknowledge means that the API will call acknowledge() for you after you've returned from your callback. In the other cases, you will have to call acknowledge() yourself.
Transactions group send or receive operations on that session (all-or-nothing). So you do send/send/send and then commit and know all 3 msgs are made available to the consumer at the time of the commit, or none. Advantages of transacted delivery: highest guarantee of delivery and you can group operations - Disadvantage: latency and possibly performance.
One main purpose of the Session is to keep a transaction's state. If your session was created to be transacted (SESSION_TRANSACTED) it knows all the messages that you are planning to send within your transaction and if that transaction is committed or rolled-back.
All the information is there in JMS Specification here
A JMS Session is an object that maintains connection to a JMS Provider for sending and receiving messages.
A transacted session supports a single series of transactions. Each transaction groups a set of produced messages and a set of consumed messages into an atomic unit of work. In effect, transactions organize a session’s input message stream and output message stream into series of atomic units. When a transaction
commits, its atomic unit of input is acknowledged and its associated atomic
unit of output is sent. If a transaction rollback is done, its produced messages
are destroyed and its consumed messages are automatically recovered.
On the other hand in a JMS Session with auto-acknowledgement, received messages, after they are delivered to application, are automatically removed from the JMS Provider without the need for application to call commit/rollback. Auto-acknowledge has no effect on a transacted session.
Currently I'm using bluelock's camel-spring-amqp component for my application.
What I want to achieve is:
Pull a message from RabbitMQ server.
Persist it to a database on successful processing / Send it to another "Error" queue on Exception
Tell the original queue that it is now safe to remove the message from the queue.
As of this writing, I'm able to pull from rabbit and persist to database using camel routes. What I don't really know how to do is acknowledge that my processing is done to the original queue. Here is my current route:
from("spring-amqp:EXCHANGE:queuename?autodelete=false&durable=true&type=direct&routingKey=")
.bean(Transform.class, "transform(byte[])")
.to("jpa:com.my.company.models.MyModel?entityType=java.util.ArrayList")
I realize I can set the acknowledgmentMode to NONE. But I don't know how to "manually" acknowledge once I have persisted my message.
Thanks for any help!
I'm new in Camel but I know a thing or two about RabbitMQ.
With RabbitMQConsumer the message is acknowledged if the processor doesn't throw any exception (line 133 at RabbitMQConsumer source).
So I suppose if you let your processor propagate the exception, the message won't be acknowledged. I haven't used spring-amqp but I guess it should have a similar behaviour.
In my application, I am using MDBs to listen to messages from a queue and then posting a request message to another queue. This posting of message to another queue happens within the MDB. Then I wait for a response using recieve method with a timeout of 20 secs. What happens now is that the request message I post to the queue is not getting delivered for 20 secs.
The message gets delivered after 20 secs but by that time the receive method returns and we are not able to process the response we receive for our request.
I am not using transacted sessions for posting the message to the queue.
Please tell me why the message is not getting delivered till the timeout period.
Using an MDB suggests that you are running within a Java EE server of some sort. Are you sure you do not have a distributed (JTA) transaction enabled for your MDB and JMS session? That would explain the request message not getting delivered until your method returns. There can be a JTA transaction enabled even though the session transaction is specified to false.
This blog post covers this case pretty good, even though it's a bit old.
The short answer is to use a UserTransaction and commit the request transaction before starting to wait for a reply.
We have producer which is producing message at a rate faster than cosumer can consume. We are using Spring JMS integration as the consumer side technology stack. Currently we are using AUTO_ACKNOWLEDGE mode.
In the onMessage() method of the listener, upon the receipt we are to planning submit the client side job to a job queue and return from the onMessage() method. This means if a) processing fails or b) our server goes down while processing there is no way for us recover.
We looked at the option of using CLIENT_ACKNOWLEDGE, but this means acknowledging a message with higher timestamp automatically acknowledges all the messages with a less timestamp. This is clearly not desirable for us because a successful processing a message with newer timestamp no way means that all the messages with older timestamp are processed completely. In effect we are looking on per message acknowledgement. However, I read somewhere that this means there is some design flaw.
The other option is to use a SessionAwareMessageListener interface provided by Spring. The contract of using this interface says that if a JMSException is thrown from the onMessage the message will be redelivered. However, I was not completely sure how to use this for our purpose.
While I dig more myself into this, any help from you guys will be greatly appreciated.
Session aware message has following onMessage prototype:
onMessage(Message message, Session session)
Invoke session.recover() for the message redelivery. Upon session.recover() will send all the unacknowledged messages back to the jms destination.