Sponsor: Using RabbitMQ or Azure Service Bus in your .NET systems? Well, you could just use their SDKs and roll your own serialization, routing, outbox, retries, and telemetry. I mean, seriously, how hard could it be?

Catch Up Subscriptions
As mentioned, I previously would use catch-up subscriptions for various use cases. One of them would be for sending emails on specific events occurring in the system. A worker process would subscribe to the $all event stream and handle incoming messages accordingly. The issue is that catch up subscriptions are controlled by the client. The start position of the subscription is stored by the client. This information must be maintained by the client. This means I did not have a built-in way to have multiple worker processes handle events from the same stream but only processing each event only once. Notice I wrote built-in above. Yes I could of created shared datasource that each worker process would share in order to determine which messages where being handled by which process. I didn’t want to go down this road. The next option would be to have multiple worker processes but each worker process would only be handling a single or small subset of events.Persistent Subscriptions

Groups
When you create a persistent subscription, you will define a group name and the event stream you want to subscribe to. When your client (consumers) want to use this subscription, they specify the same group name and the stream.Acknowledge/Not Acknowledge/Timeout
I’ve rewritten the above thanks to Greg Young’s correction in the comments.
The server will dispatch N events to the consumer at any given time. The number of events is configurable when connecting to the subscription as you can define a buffer size which is the number of in-flight messages the client is allowed. It can also not acknowledge the message, or if a timeout period expires without either a retry can occur.