Replies: 8 comments 12 replies
yeah your issue here might be that the producer gets rate-limited by how quickly messages are being ACKed on the sender side. That's a great feature to have to ensure high deliverability but yeah, that might impact your throughput. Also, using the durable event-sourced queue will slow things down too because that adds a layer of persistence to both the SEND and the ACK. The internal default buffer size is quite large though - so you should have head-room to let it rip, unless you're already hitting that value I suppose. How big are your CSV rows?
Use stashing to solve this: https://www.youtube.com/watch?v=S8FNHZ7SES8 |
|
It should be around 100–200 bytes per row, I think. Here’s an example row: In total, I have around 2000–3000 rows. This is the class being used: Thanks for the suggestion on stashing — I’ve added that to the producer when it's idle. However, when I import the file, the messages get stashed but I never receive a ShardingProducerController.RequestNext message. Right after stashing, I start getting the following error in the logs: This starts happening very quickly after startup. I’m attaching the full logs below for reference And this is how I configured persistence — since the error seems related to this: |
|
Thanks for the detailed reply! I'll go ahead and give Akka.Streams a try for this use case — especially following the pattern in the RabbitMqConsumerActor sample you linked. The idea of streaming rows from CSV and piping them into the sharding system via Ask makes a lot of sense, and it could help bypass some of the friction I'm seeing with DurableConsumers. In the meantime, I put together a minimal repro project that consistently triggers the issue I'm encountering with Akka.Cluster.Sharding.Delivery. You can find it here: https://github.com/vulcanUPB/AkkaShardingEventFromCSV - It's not necessarily the cleanest codebase — it was quickly assembled to isolate the issue — but it should still provide a reproducible context. There's definitely room to make it cleaner and more idiomatic, but I hope it's useful for debugging purposes Let me know if there's anything you'd like me to tweak or isolate further. Thanks again! |
|
Here's a message throughput benchmark for Akka.Delivery with sharding and durable queue, using in memory journal and snapshot store:
|
|
Akka.Cluster.Delivery message throughput benchmark without event sourced queue:
I think it is inherently slow as it is |
|
I want to chime in that I experienced the first issue (dropping messages) weeks ago when implementing my producer according to the following example: https://github.com/akkadotnet/akka.net/blob/dev/src/examples/Cluster/ClusterSharding/ShoppingCart/Producer.cs#L84 I also tried fixing it by stashing the domain message while in Idle and then unstashing all before (if I remember correctly) becoming Active but had issues with that as well. In my scenario I have an Akka.Hosting Fortunately I fixed my issues by removing the Idle state from the producer actor so it does not have switchable behaviors. It contains just the |
|
@danne931 can you share your producer message handler code? |
|
@Arkatufus I thought my code was fine when sending ~1000 messages but now I see delivery issues when sending several thousand. I fixed the delivery issues by revising my code to use stashing and behavior switching. I created a C# example here. public record AtLeastOnceDeliveryMessage<Msg>(Guid EntityId, Msg Message);
public class ClusterShardingProducerActor<Msg> : ReceiveActor, IWithUnboundedStash
{
public IStash Stash { get; set; } = null!;
public IActorRef SendNext { get; set; } = ActorRefs.Nobody;
public ClusterShardingProducerActor()
{
Idle();
}
private void Idle()
{
Receive<AtLeastOnceDeliveryMessage<Msg>>(_ =>
{
Stash.Stash();
});
Receive<ShardingProducerController.RequestNext<Msg>>(next =>
{
SendNext = next.SendNextTo;
Become(Active);
Stash.UnstashAll();
});
}
private void Active()
{
Receive<AtLeastOnceDeliveryMessage<Msg>>(cmd =>
{
SendNext.Tell(new ShardingEnvelope(cmd.EntityId.ToString(), cmd.Message!));
Become(Idle); // Wait for demand
});
Receive<ShardingProducerController.RequestNext<Msg>>(next =>
{
SendNext = next.SendNextTo;
});
}
}However, I am not sure if it's all the |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Hello,
I'm building a high-throughput message ingestion system using ShardingProducerController, targeting a sharded ProductActor region. I’ve encountered two main challenges:
Use Case
I register a single producer actor (ProductProducerActor) at system startup using the WithActors(...) hook like this:
Then, I initialize the ShardingProducerController inside the AddStartup(...) hook, after the cluster is fully joined:
Problem 1: Slow throughput with a single producer
My registered ProductProducerActor sends messages to sharded entities by waiting for RequestNext and responding with a single message each time. Here’s a simplified version:
The issue:
Problem 2: Race condition when dynamically creating producers
To work around the bottleneck, I experimented with spawning multiple producer actors dynamically (one per CSV row) using a service like this:
The issue:
The Start message takes time to reach and initialize the producer.
If I immediately send a message to the producer (e.g., AddProductProtocol), it's often dropped because the actor is still in Idle() and hasn’t received its first RequestNext.
Questions
Any advice or best practices around high-throughput ingestion with reliable delivery would be greatly appreciated.
Thanks
All reactions