Same routing behavior on different nodes/routers - scala

I know, that if I use a consistent hashing group router, it will always rout to the same registered routees.
So I wrote my application, with a few routees on there own routee-nodes and a public-node with a router, which is reachable by the client.
If the client sends a message it is routed as it should be and it works fine.
Now what I want to do is, add more public-nodes with thier own router that provides the same sending/routing behavior as every other public-node.
What I mean is, that it should not matter if a client sends message XYZ to public-node A, B or C. It should always go to the same routee-node.
At first I thought that akka may provides this behavior by default, because:
I used a group and not a pool router, so everyone knows the same routees
I leared that cluster nodes could be ordered
So I assumed that the routees list is ordered and the different routers map the same keys to the same routees. But testing showed me that I was wrong.
So, is there way in akka to achieve this behavior? Thanks.

As I expected, this behavior shoud be the standart for consistent hashing group routers and it is a bug in the akka-cluster package (current version 2.3.0-RC1)
See this tickt and this google-group post for more details.

Related

Bidirectional REQ/REP on a single port with ZeroMQ

I'm struggling with the following problem:
I'd like to make bidirectional asynchronous requests/replies between N clients and 1 server with ZeroMQ.
This means that any client can make a request to the server and the server must reply to the client.
In the other way, the server must be able to make a request to any of the identified clients and the client to reply to the server.
I think I must use routers/dealers, but I'm not sure I need them in both ways.
Moreover, is there a way to have this whole paradigm using only one port on the server side and on each client side?
Q: is there a way to have this using only one port on the server / client side?
Well, this is the simpler part. No, this is not achievable. Having just one telephone box in the college will ring in the hallway, but will never help to address a call to the required department ( it would not correctly reach the intended professor of Quantum Mechanics instead of the one in Fine Arts ).
Using but one port means having a chance to expose for public access a one and only one ZeroMQ Scalable Formal Communication Pattern Archetype AccessPoint and ( except the very specific PAIR/PAIR distributed behaviour Archetype ) there is always some kind of hard-wired distributed-behaviour of the interconnected agents' AccessPoint-s.
This means, using one port gives just one and only one kind of such distributed-behaviour, not a mix of them.
This also answers your first part. If a REQ/REP distributed-behaviour Archetype was used in a direction from client-nodes towards the server, and another REQ/REP distributed-behaviour Archetype was to be used in an opposite direction from server towards the client-nodes, these ( directed ) services cannot co-exist on the same address:port.
BONUS PART : A kind of Life-Saving, but a bit dirty Trick( Not to be used for Medical and/or Emergency Systems )one may sort of supersample one and only one of the REQ/REP messaging directions and add a tricky "quasi-protocol" so as to serve this same channel for the both intended signalling directions. If sending sufficiently enough protocol-messages from one side, be it { client | server } the REQ/REP message initiator will simply send NOP-messages often enough to permit the REP-side replying party to "quasi-initiate" its "quasi-REQ-message" when still being the authentic-REP-AccessPoint in the single REQ/REP distributed-behaivour Archetype.Yes, performance and resources use are a cost for doing this, but a careful soft-real-time system design practices will help to make this work, if your needs are extremely dependent on using but one port and if your consciousness and your deployment ecosystem may tolerate the increased traffic patterns of such supersampled-REQ/REP data-flows
You might also like posts, here on Stack Overflow, about unavoidable mutual deadlocks, the distributed REQ/REQ FSA-s will fall into.
ZeroMQ hierarchy explained in less than a five seconds
UN-AVOIDABLE DEADLOCKS

Combining a BackoffSupervisor with a Router

Can I combine a BackoffSupervisor with a Router?
The concepts seem very similar, but it seems as though a BackoffSupervisor isn't itself a SupervisionStrategy I can give to the Router.
I can wrap the Props used by a Router in a BackoffSupervisor, so each child in the Router is actually the pair of supervisor/actor. But, this breaks the SmallestMailboxPool, probably among others, because the intermediate supervisor actor's mailbox is always empty. (it just forwards to the actor it supervises)
The goal being, of course, a Router that restarts the children with a backoff.
It isn't really possible implementing back off supervision as a supervision strategy as those needs to decide directly how to deal with failure. This means that if you want to combine that with routing where the routing strategy uses some information about the recipient mailbox you will have to implement your own router taking care of these two concerns.
Note though that the routers that does not inspect mailboxes will work fine routing to BackoffSupervisor-protected actors. (So, no problem if you use RoundRobin, Random, Broadcast, ScatterGatherFirstCompleted, TailChoppingPool or ConsistentHashing).

Starting Actors on-demand by identifier in Akka

I'm currently implementing a system that that receives inbound messages from an external monitoring system. I'm translating these messages into more concise 'events', and I'm using these to alter the state of 'Managed System' objects. Akka Actors seemed like a good use case for encapsulating mutable state in concurrent applications.
The managed systems are identified by a name (99% of the time this is a hostname). Whenever a proper event is received, the system routes the message to the correct actor based on the name property. At first I used to use actorSelection and the complete paths of said actors, but that was very ugly, and I saw several people advise against relying on the fully qualified name of an actor to deliver message.
So I've set up a simple EventBus, which is great as I can now simply do:
eventBus.subscribe(subscriber1, "/managedSystem01")
eventBus.subscribe(subscriber2, "/managedSystem02")
eventBus.publish(MonitoringEvent("/managedSystem01", MonitoringMessage("managedSystem01", "N", "CPU_LOAD_HIGH", True)))
eventBus.publish(MonitoringEvent("/managedSystem02", MonitoringMessage("managedSystem02", "Y", "DISK_USAGE_HIGH", True)))
Of course, I now have the issue that, should I receive and event that concerns a managed system for which I've not spawned an actor yet (this is entirely possibly, it is impossible for me to get an absolute list of managed systems unfortunately), the message will be routed to the dead-letter mailbox.
Ideally I don't want this to happen. When it is unable to address a specific actor, I want to spawn a new one dynamically.
I suppose that, theoretically, I could subscribe to DeadLetter messages but:
That sounds a little 'hacky', since those message are essentially reserved for the system
Is it even possible to recover the original message (in my case, the MonitoringMessage) that is sent to the DeadLetter mailbox?
Alternatively is there a way to check if there are ZERO subscribers to a certain "topic"?
What you describe ("send to Actor by some identifier, if it does not exist buffer until it gets created and then deliver to that newly on-demand created Actor") is implemented in Akka as Cluster Sharding.
While it is designed primarily for sharding load (work) across a cluster, you could use it locally as well, since your requirement is essentially a scaled down (to one node) version of problem that it solves. It takes care of starting new Actors if they don't exist for a given identifier etc, so you'd simply subscribe the shard-region to the events and it'll take care of creating the actors for you.

Forward message to next Round Robin routee

I have this Play app that connects to a distant server in order to consume a given API. In order to load balance my requests to the distant server, I connect multiple accounts to that same server. Each account can query the API a given number of times. Each account is handled by an Akka actor and these actors are behind an Akka Round Robin router. Thus when wanting to consume the distant API, I "ask" the RR router for the wanted info.
This implementation runs fine until, one account gets disconnected. Basically, when one account is disconnected, the actor returns a given object that says "something was wrong with the connection", and then I send a second request to the RR router again to be handled by another account.
My question is, instead of having to have the "retry" logic outside the router-routee group, is there a way to do it inside? I am thinking that for example at router level, define a logic that handles these "something was wrong with the connection" messages by automatically forwarding the request to the next routee to be handled by it, and only return a final response once all routees have been tried and none worked?
Does Akka provide a simple way of achieving this or should I just carry on with my implementation?
I'm not sure if I fully understand your design but I think you should try using first complete model supported by the ScatterGatherFirstCompleted routing logic.
router.type-mapping {
...
scatter-gather-pool = "akka.routing.ScatterGatherFirstCompletedPool"
scatter-gather-group = "akka.routing.ScatterGatherFirstCompletedGroup"
..
}
in the simple form
---> Route
--> SGFC-1
RR ->
or possibly combined with round robin router.
---> Route
--> SGFC-1
RR ->
--> SGFC-2
---> Route
The same as in your proposal connections are represented by the routes. SGFC-1 and SGFC-2 should have access to the same pool of routees (conenctions.) or part of the pool.

Select remote actor on random port

In Scala, if I register a remote actor using alive(0), the actor is registered at a random port.
I can do the registration like this: register('fooParActor, self) in the act method.
Now, on the master/server side, I can select an actor by supplying the port. Do I need to manually scan the ports in order to use random ports?
The problem I am trying to solve is to create n actors on a node and then select them all at a master/server program, e.g. start 10 slaves on node x and get a list 10 remote actors at node y.
How is this done?
There's no need to register various ports for the actors. Instead you need one port for the whole actor system - more precisely the akka kernel (that the server needs to know too). See this page of the documentation for how all of this works in detail.
In order to select a remote actor you can then look it up via its path in the remote actor system, similarly to something like this:
context.actorFor("akka://actorSystemName#10.0.0.1:2552/user/someActorName/1")
In that case, you would have created the n actors as children of the someActorName actor and given them the names 1 to n (so you could get the others via .../someActorName/2, .../someActorName/3 and so on).
There's no need to randomize anything at all here and given how you described the problem, there is also no need for randomization within that. You simply start the 10 actors up and number them from 1 to 10. Any random numbers would just unnecessarily complicate things.
As for really random ports I can only agree with sourcedelica. You need a fixed port to communicate the random ones, or some other way of communications. If someone doesn't know where to communicate to due to the random port it simply won't work.
You need to have at least one ActorSystem with a well known port. Then the other ActorSystems can use port 0 to have Akka assign a random port. The slave ActorSystems will have actors register with an actor on the Master so it knows all of the remote systems.
If you absolutely need to have your master use a random port it will need to communicate its port out of band (using a shared filesystem or database).