Orleans. Akka.net. Problem with understanding the actor model - c#

If you don't know C# but you are familiar with the actor model please read my problem below as it is more about architecture and data management.
I'm a very junior C# developer and trying to understand what the actor model is. I'm kind of done with it but it's left one point that I cannot get.
Before I tell you a problem let me describe the context in order to provide you with better undersrtanding.
As a test example I want to build an app for an imaginary bank. I'm going to implement this application by using both akka.net and Orleans on learning purpose and to be able to compare them.
Use cases:
As a user I want to be able to create a new account;
As a user I want to be able to login to the app using my account unique number;
As a user I want to be able to deposit money to my account;
As a user I want to be able to select another user and transfer a specified sum of money to their account;
As a user I want to be able to withdraw a sum of money from my account.
So, there are the following entities:
User;
Account.
Identifying one to one relationship between the user and their account.
I'm going to use ORM to store this data in my Database. Obviously the models look something like this:
public class User
{
public Guid Id { get; set; }
public string FullName { get; set; }
....
}
public class Account
{
public Guid Id { get; set; }
public string UniqueNumber { get; set; }
public string Balance { get; set; }
...
}
And I also want to have two actors/grains:
AccountActor;
TransactionService;
Their interfaces:
//Or IAccountGrain
public interface IAccountActor
{
void Deposit(Money amount);
void Withdraw(Money amount);
}
//Or ITransactionGrain
public interface ITransactionActor
{
void Transfer(IAccountActor from, IAccountActor to, Money amount);
}
The thing that I don't understand is how to treat the data in the relation database.
Let's imagine the following scenario:
50 users are online and vigorously making request via the client app to the REST API of the app.
They are withdrawing, depositing and transferring money almost without any pauses.
The question is:
Should I create one actor per user account? I'm pretty sure that I
need because how then I can implement thousands of transactions
between different accounts.
How can I associate the user account with the AccountActor? Is it correct If I load the data from database using repository before
actor's activation/start and set up the state?
And the main problem:
How to save state back to the database?
Let's image an account A that has 1000$.
And It occurs about 100 transactions initiated by the users where this account is involved.
Account A changes its state from message to message.
What is the best approach to save these changes to database? I read that If I use calls to the database directly from the actor, I will lose all the benefits because of blocked operations.
Should I create one more actor to process messages from other actors and writes changes to database using repositories?
I mean I can send from AccountActor messages about the account changes to the new actor where I will call the appropriate repository. But, Isn't it a bottleneck? Let's imagine 1000 users online and about 100 000 transactions between accounts. Then the actor that is responsible for saving accounts changes to the database can have too many messages to process.
Sorry for the long text. I tried to find examples of applications that use Orleans or Akka.net but I haven't found anything what utilizes a database.
Thank you for you attention.

There are a couple ideas you’re missing here, but let’s take the questions in order.
Should I create one actor per user account? I'm pretty sure that I
need because how then I can implement thousands of transactions
between different accounts.
I assume the alternative you’re considering is multiple actors per user account, and this would be wrong. There can be only one actor per user account or else you will run into the problem you describe that simultaneous requests can withdraw the same money twice.
How can I associate the user account with the AccountActor?
You are missing a UserActor which owns the AccountActors. An account cannot exist without an owner, otherwise we don’t know who owns the money in an account. In the real world, one typically does not send money to a random account. They want to send it to a person, and uses the senders User persona account to do so.
Is it correct If I load the data from database using repository before
actor's activation/start and set up the state?
Yes, in fact that is mandatory. Without the state in the actor, the actor is not good for much.
What is the best approach to save these changes to database? I read
that If I use calls to the database directly from the actor, I will
lose all the benefits because of blocked operations. Should I create
one more actor to process messages from other actors and writes
changes to database using repositories?
You’re on the right track but not quite there yet. Saves of actor state are done with asynchronous methods to write to the DB. With an async method, the main thread does not block waiting for the DB write to happen, thus the processing thread can continue with its business.
When only one actor is involved in an action, it can save its own state via the async method. In banking registers there are always 2 accounts involved and writes to both must succeed or fail, never one succeed and one fail. Therefore, the TransactionActor will open a DB transaction and tell each AccountActor to save its state using that DB transaction. If either fails, it aborts the transaction and both fail. Note that this method is a private async method on the TransactionActor so you get the benefits of parallel processing.
BTW, you can't find any examples of writing to the DB in Orleans because that is all handled for you by the framework. The save methods are automatically async and they interact with the DB. All you do in Orleans is reference the Actor and the state is automatically pulled from the DB for you.

Related

Architecture for passing messages from one system to another

I am starting to work on a piece of development today, and was looking to see if this is the optimal solution:
I have 3 systems:
SystemA : third party system which handles order management.
SystemB : internal system used for shipping orders that have been placed on systemA for a particular supplier (used for shipping old suppliers orders)
SystemC : internal system used for shipping orders that have been placed on systemA for a particular supplier. (used for shipping new suppliers orders)
So systemB and systemC both need to pull orders from systemA but only want to pull orders based on a systemID, which is fine.
Goals:
1. Avoid Duplication of orders sent to old (B) and new (C) systems
2. speed
3. Allow for new systems to be added
I plan on building an intermediary service the retrieves orders from systemA using BL that I already have written. From there it will add it to a queue. System B and C will them make a web request to some endpoint using their system ID to get their latest messages for their system. Each system can them process the messages/orders and import them into their systems.
So my questions:
Would masstransit or nservicebus work in this situation?
IS there an example of this sort of thing anywherE? I could not find one?
Does this structure sound ok or would there be a better way to do it?
Would I be better using a table queue and WCF to acheive this?

Unit of work/Transaction within an application service method?

I understand how to use entity framework to implement a unit of work and only commit changes after the full unit is performed, but how do i take this a step further? For example, the following needs to all happen under one transaction
CreateUser(...)
{
//1.) New up user object
//2.) Add newly created object to database
//3.) Send Email
//4.) Commit transaction ( ensures email is successfully sent AND object is created in database, else transaction fails
}
I'm not quite sure how to make sure that sending of email and saving of the user within the database all happens within one transaction. Any advice is greatly appreciated
Not all resources are transactional. Email is one of them. So one cannot expect sending email is rolled back when other resources fails in a transaction.
There are some alternative solutions:
1) invoke non-transactional resource at the last step.
Just like you did in the create user example. In this case, all business constraints are checked and pass, the only reason of failure is infrastructure issue which does not frequently happen in real world(with carefully maintainence). Make some compensation when the infrastructure failure does happen. This could be done automatically or manually, depends on how often it happens. For example, if the email is sent, but the user setup is rolled back, you may tell him/her that you're very sorry(this is very import :P ) but there is something wrong with the setup, please try again.
2) apply eventually consistency
Use transactional resource instead to trigger the process. For example, you may use messaging (globally transactional if using 2 phase commit) to notify to send emails.
CreateUser(...)
{
//1.) New up user object
//2.) Add newly created object to database
//3.) Publish user setup event by messaging
//4.) Commit transaction ( ensures message is successfully sent AND object is created in database, else transaction fails
}
In any case, failures and unconsistency occurs more or less. You need to evalute is it acceptable.

Monitoring Windows service status remotely as non-administrator?

Background: I have an application that, among other parts of its back-end, uses a server-side Windows service to do some of the computation.
What I'm trying to do is display the status of the service (Running vs. Stopped, essentially) on the client, such that the users can know (a) if the background computation is happening, or (b) if they need to go poke their IT guy to check the server. (It's written for an SME customer that doesn't have a full-time IT department or a budget that wants to be spent on fancy service-monitoring-and-alerting software.)
In itself, that's easy enough to do with ServiceController - if you're an administrator on the server, which the users, of course, aren't. Is there a way to read service status from a remote server in .NET as a non-administrative user? (All I need is to read the status; I don't need, and actually specifically don't want, to give the users the rights to stop/restart/alter the service in any way.)
If the user under which your application works doesn't have sufficient permissions for accessing services, you're likely to get an error like this:
service.Status threw an exception of type 'System.InvalidOperationException'
Cannot open MyService service on computer '192.168.0.7'. Access is denied.
You need to switch to another user context to be able to monitor it. If you don't want to do it for entire application (which is rather obvious), try impersonation for the actual piece of code which does the status checking. What should be the user? Actually for the safety reasons, it definitely shouldn't be the user who has an access for entire machine. It should has access just for controlling the services. Ask the administrator to create such a user for you. The status monitoring can be then executed like this:
public string GetServiceStatus(string machine, string service)
{
return Impersonate(
() =>
{
var service = new ServiceController(machine, service);
service.Refresh();
return service.Status;
}, USER, DOMAIN, PASSWORD
);
}
The entire thread with detailed solution can be found here.
Edit:
Let me explain the topic further. The solution I've provided gives the opportunity to change the user context, for some particular piece of code. It can be whatever you want e.g. service status checking. User, under context such an operation is going to be executed, can have granted the access to perform it, or not. It's completely different story though. It's the computer administrator responsibility to grant such an access. In the simplest case he can just add such a user to the Administrators group, which will be reckless, but he can also grant granular access using Group Policy. More detailed information regarding such an administration issues can be found here and here.

Edit option in Deal for Wallet in Windows Phone 8

So far i am developing the Windows Phone application that make an Wallet API to work for Wallet in Windows Phone 8. Normally we can use Deal object to add the deals into the Wallet application. But in every attempt, added deal does not have an option to edit. However if it is added through build in Wallet app, Obviously it has an Edit option. I am cracking my head for this problem for a long time. Could you please any one can help me to enable the Edit option through Deal API ?
That's intentional. Think about the zen of a wallet. When a user inputs a deal manually it doesn't mean anything to anyoe but the user. When your app creates a deal it means something to other people (to cashier looking at it, the machine scanning the barcode, etc). So it's important your deals don't get changed by the user.
Your locally stored deals on the phone are only a facade to deals stored on the server. So if you want to update those , you'll need to create a WalletAgent, pull data from a remote source and update those deals in the background.
Here's a code sample of a trivial WalletAgent that always marks all your wallet items as "Attention needed". Obviously this would be a good place to query a remote web service and update your WalletItem properties.
public class myWalletAgent : WalletAgent
{
protected override void OnRefreshData(RefreshDataEventArgs args)
{
foreach (WalletItem item in args.Items)
{
item.SetUserAttentionRequiredNotification(true);
}
base.OnRefreshData(args);
NotifyComplete();
}
}

Custom Workflow Step - Retrieve Field From Entity

I have a workflow activity which I use to send out e-mails to suppliers on a regular basis and scheduling follow-up phone calls etc if there is no reply. However my problem is that I somehow need to retrieve the e-mail for each supplier before sending the message.
How can I go about retrieving the value in a specific field from a specific entity in CRM 2011?
Here is the pseudo-code:
For each supplier entity in the system {
if (sendSalesRequest) {
Send Initial E-mail
- wait 21 days
if (noReply) {
Send Follow up e-mail
- wait 7 days
if (noFollowupReply) {
schedule phone call activity everyday until reply
}
}
}
}
However, I need a way of retrieving the e-mail address from the suppler entity.
I'm not looking for a worked solution (though I wouldn't turn it down!), just guidelines on how to go about this task as I'm brand new to CRM development.
Thanks,
Tysin
If your new to 2011 I would suggest reading about the SDK. In particular you need to look at custom workflow activity development. It has a number of samples which should help you along the way.
I would suggest having:
A master workflow, that has a single step to call your custom workflow activity which processes the suppliers. You start this workflow whenever you want to process all the suppliers.
An on-demand workflow that sends the initial email
An on-demand workflow that sends the follow up email
An on-demand workflow that schedules the phone call
The reason I suggest this is that means you keep the creation of data in workflows where is it easier to manage and handle. E.g. you could change the email message without recompiling your custom workflow activity.
So first step in your master custom workflow activity, is to query Crm, I would suggest a QueryExpression to start with. That way you can get the data to base you logic on.
Then you need some code to start a workflow against a record, to send the email and phones calls. There is an example for this here.
Then you code would look a little like this:
QueryExpression query = new QueryExpression("supplier");
query.ColumnSet = new ColumnSet("sendsalesrequest", "noreply", "nofollowupreply");
EntityCollection entities = service.RetrieveMultiple(query);
entities.Entities.ToList().ForEach(entity =>
{
if(entity["sendsalesrequest"] == "Yes")
{
StartWorkflow(entity.Id, "Send Initial E Mail Workflow Name");
}
//etc
}
This is quite high level but should hopefully get you going in the right direction.

Categories

Resources