Open utterances-bot opened 4 years ago
Hello, thank you for the article. Could you please explain what is the profit of using stateless workers if they still call 1 state-full grain which is single threaded.
Hi @allieberezhnaya -- for some reason github stopped notifying me of replies for awhile, sorry for the late response.
Orleans will spin up multiple stateless workers, and requests to stateful grains are queued (actors are message-driven), so it isn't a bottleneck like it might appear at first glance. But mostly it's just a clean "separation of concerns" decision (like CQRS itself). In a real system you'd have some sort of gateway / aggregator business-operations API that clients would call, then (as demonstrated here) those would invoke the CQRS services, and the event sourcing would effectively be a microservice behind CQRS. Also, of course, Orleans is free to host that stateful grain anywhere in the cluster whereas a stateless grain is always hosted locally to the silo servicing the calling client.
Hi @MV10, thanks so much for the extensive write-up! Blogs like yours are a gold mine for developers trying to get familiar with technologies like Orleans.
While extending your OrleansEventStreamLog example I ran into a concurrency problem I hope you'll be able to help me out with. In the CustomerCommands class (marked as StatelessWorker and Reentrant) methods can be executed concurrently. In case of the method PostAccountTransaction, this means the following flow is possible:
I've written some code to simulate this problem. The code can be inserted as-is on line 70 of DemoClient/Program.cs. If it doesn't simulate the problem on your PC perhaps an await Task.Delay(100) in ServiceCustomerAPI/CustomerCommands.cs:173 would do the trick.
var accountNumber = "1";
var account = new Account
{
AccountNumber = accountNumber,
AccountType = "Checking",
Balance = 100,
IsPrimaryAccount = true
};
var addAccount = await cmd.AddAccount(id, account);
if (addAccount.Success)
{
var postFirstTransactionTask = cmd.PostAccountTransaction(id, accountNumber, -60);
var postSecondTransactionTask = cmd.PostAccountTransaction(id, accountNumber, -60);
await Task.WhenAll(postFirstTransactionTask, postSecondTransactionTask);
var postFirstTransaction = postSecondTransactionTask.Result;
var postSecondTransaction = postSecondTransactionTask.Result;
if (postFirstTransaction.Success)
{
var balance = postFirstTransaction.Output.Accounts.Single().Balance;
Console.WriteLine($"First transaction posted, new balance: {balance}");
}
else
{
Console.WriteLine($"Unable to post first transaction:\n{postFirstTransaction.Message}");
}
if (postSecondTransaction.Success)
{
var balance = postSecondTransaction.Output.Accounts.Single().Balance;
Console.WriteLine($"Second transaction posted, new balance: {balance}");
}
else
{
Console.WriteLine($"Unable to post second transaction:\n{postSecondTransaction.Message}");
}
}
else
{
Console.WriteLine($"Unable to add account:\n{addAccount.Message}");
}
To me it seems like this business logic, such as checking for sufficient funds, should live somewhere inside a stateful grain which represents the Account (or the Customer and their accounts), but perhaps I'm missing something.
@wdkr Thanks for the note. I'm not currently working with Orleans at all these days (unfortunately), so I'm not likely to revisit this code or the article -- but you're right, the scenario you describe is possible. This is why the world of finance (which is what I do in the real world) still has concepts like pending transactions and batch (overnight) posting and reconciliation.
That's effectively the same thing as have a $100 account balance and writing two checks for $60, it just goes sideways more quickly (progress!).
This is a really good article Jon. Thanks for sharing. I'm struggling to get an Event Sourcing project with Orleans running. Not so much for the logic but on the cluster configuration. Do you do consulting? We'd really appreciate you could help us to get the ball rolling.
@ccerrato147 Hi Carlos, thanks for the kind words. I'm no longer using Orleans (it changes pretty quickly, which is both good and bad!), and I don't do any consulting, sorry. Good luck with your project.
@ccerrato147 Hi Carlos, thanks for the kind words. I'm no longer using Orleans (it changes pretty quickly, which is both good and bad!), and I don't do any consulting, sorry. Good luck with your project.
Yes, definitely changes with frequency. As you say both good and bad. I have to piece together from multiple tutorials and sources. If by any chance you know someone that does do consulting and is an active Orleans user then I'd appreciate the referral. Thank you for your good wishes Jon.
@ccerrato147 Let's have a chat about consultations.
I sent you an email.
Event Sourcing with Orleans Journaled Grains - Forty Years of Code
Event sourcing with logging and snapshots for Microsoft Orleans
https://mcguirev10.com/2019/12/05/event-sourcing-with-orleans-journaled-grains.html