minghuaw / fe2o3-amqp

A rust implementation of the AMQP1.0 protocol based on serde and tokio.
MIT License
64 stars 7 forks source link

Does this implementation require a connection pool? #239

Open lsunsi opened 7 months ago

lsunsi commented 7 months ago

It's probably a dumb question, but do I need to have multiple connections for a large application or a single one is enough?

Does the Session concept relate in any way to my question since it can multiplex operations from what I understood?

In case a connection pool make sense, is there any implementation to go with this lib? Would integrating with Deadpool library make sense?

minghuaw commented 7 months ago

I think it's really going to depend on you use case. This crate simply implements the protocol.

I think in most use cases, long-lived connections are used for sending and receiving messages, which is probably not the best place to use a connection pool. Because AMQP has all the handshakes for creating Connection, Session, and Link, I don't think the protocol is designed for use cases where short-lived connections are frequently created and dropped. You will likely see a worse performance in such use cases.

The Session type is more of a grouping of multiple links where messages are multiplexed, and provide some flow control for this group of links, but the state is still stored within each individual link. However, all the AMQP 1.0 message brokers/services don't seem to make much use of the session at all.

If you were to pursue a use case with connection pool, I think anything that gives you a pool of tokio compatible connection (tokio's AsyncRead + AsyncWrite) would work. You can set up an AMQP Connection directly with a stream using this method open_with_stream

minghuaw commented 7 months ago

Or if you were thinking about a pool of AMQP Connections, I think it's possible with the current API, but I don't think it's the recommended usage. The way you would want to reconnect a link to another connection is essentially the link recovery process, which again is not designed for frequently creating short-lived senders and receivers.

All sessions (and thus their links) that share the same Connection are multiplexed. I think some simple experimentation is needed to see whether having dedicated connection per sender/receiver or sharing a multiplexed connection with a group of sender/receivers gives the best throughput. In an related issue (https://github.com/minghuaw/azeventhubs/issues/9), the best performance was achieved with dedicated connections for each sender/receiver

minghuaw commented 7 months ago

Here is a good article explaining the protocol without diving into the 100+ pages long spec (https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-amqp-protocol-guide)

I remember seeing folks from Azure messaging (probably Service Bus) NOT recommending using AMQP as if it is HTTP because there are more handshakes. But for long-lived connections, AMQP is more efficient and performant in their context which is comparing using AMQP vs REST API with Service Bus

lsunsi commented 7 months ago

Wow what an amazing answer. Absolutely, this makes sense. I'll read the documentation and get to know better. Thanks a lot!

I might report back to this issue if I get more concrete and useful information. I'll use this library to setup a big project I have with activemq artemis, so I might have some insights after that!

minghuaw commented 7 months ago

Feel free to keep this issue open for questions and feedback. There are probably use cases that I had not considered, and I will be more than happy to help you if you need some new feature for your use case

lsunsi commented 7 months ago

Seriously, thank you! This interaction was unexpectedly wholesome and improved on my Saturday haha

lsunsi commented 7 months ago

@minghuaw One more question for you. I'm gonna use this library in an application with about 20 senders and 20 receivers at all times. Should I create a single session to attach all of them, or one by one? I know this question is more about AMQP 1.0 then the library, but I'm having trouble understanding the positioning of the session concept within it all.

minghuaw commented 7 months ago

I would say having one session per sender/receiver would likely be the better option (if memory is not too limited). This really comes down to two reasons.

  1. On the protocol side, the Session concept was introduced into AMQP protocol to do flow control. So a group of senders sharing the same session would mean they are subject to the same flow control and a few frequent sender may inadvertently affect other senders sharing the same session.
  2. For the library itself, the session event loop handles updating the state of the unsettled messages, which is a shared state between the session and the sender/receiver. Having a large number of sender/receiver is probably going to increase the chance that some may have to wait for the lock on the shared state to be released.

The shared state as well as the channels to the link are all stored in HashMaps in the session event loop, but it shouldn't be a big problem unless you have a really limited memory to work with.

lsunsi commented 7 months ago

@minghuaw Thanks again for the quick response. Another thing, the Connection and Session objects are not related (lifetime wise) with the Receiver/Sender ones. If I have a Sender, do I have to keep the Connection and Session objects alive so it works, or it would work even independently of the previous objects?

The reason I ask is because I don't know whether to hold the Session alongside the Sender in my structs or not. Sorry for the newb questions.

minghuaw commented 7 months ago

@minghuaw Thanks again for the quick response. Another thing, the Connection and Session objects are not related (lifetime wise) with the Receiver/Sender ones. If I have a Sender, do I have to keep the Connection and Session objects alive so it works, or it would work even independently of the previous objects?

The reason I ask is because I don't know whether to hold the Session alongside the Sender in my structs or not. Sorry for the newb questions.

You would want to keep the connection and session variables until you close the links(sender/receiver), and you would want to close the session before closing the connection. This is an unfortunate design choice. Channels are used to transmit messages between connection, session and links, and the channels themselves don't carry explicit lifetimes. Another problem is if explicit lifetime was introduced to links, it would make spawning multiple sender and receiver really convoluted because spawn generally requires something that has 'static lifetime

lsunsi commented 7 months ago

Yeah this makes sense. Another last question, and I'm sorry to keep bothering you. Is it ok to keep Sender/Receiver around for long times after initialization?

I think the flow of my program will be something like, spawning the 20 session and senders/receivers at boot time, keeping all structs alive while we are at it, and just keep using them for as long as the software runs.

minghuaw commented 7 months ago

That should be fine on the library side. However, you would probably want to check if there's any settings regarding the idle time on your message broker (this may also be different for sender and receiver). For example, i think it's Service Bus or Event Hubs that enforces a 30 mins idle time limit for senders.