How to guarantee message delivery in a non-transacted, lightweight environment?
For example:
How to not loose the message (avoid not send the message) in this situation?
Edit: The message must be delivery exactly once.
In this scenario you have 2 shared resources (database and queue) and you want them to be transacted together. You want your database to commit if the message sent to the queue. You want your database not to commit if it is not successfully sent and vice versa. This is simply global transaction mechanism like 2PC. However to implement a global transaction mechanism is not that easy and it is also very costly.
I suggest you to implement at least one strategy on producer side and idempotency on consumer side to provide consistency.
You should create a message table on producer side's database and persist messages to this table before sending to queue. Then with a scheduled thread (here there may have multiple threads to increase throughput but be careful if your messages needs to be consumed in the order they produced) or any other thing you can send them to queue and mark them as sent to ensure that the messages which are already sent will not be sent again. Even if you do that there might be some cases that your messages are sent more than once (e.g. you send a message to queue and your application crashed before marking the message as sent). But it is not a problem, because we already want to implement at least once strategy on producer side which means we want a message to be sent to queue at least once.
To prevent a consumer to consume same messages which are produced more than once on producer side you should implement idempotent consumers. Simply, you can save id of consumed messages to a database table on consumer side and before processing messages coming from the queue, you may check if it is already consumed. If it is already consumed you should ignore it and get the next message.
There are of course other options to provide consistency in microservices environment. You can find other solutions on this great blog - https://www.nginx.com/blog/event-driven-data-management-microservices/. The solution i explained above also exists in this blog. You can find it in Publishing Events Using Local Transactions section.