Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialising an actor system with cyclic references #30

Closed
fatuhoku opened this issue Sep 17, 2013 · 4 comments
Closed

Initialising an actor system with cyclic references #30

fatuhoku opened this issue Sep 17, 2013 · 4 comments

Comments

@fatuhoku
Copy link

Pykka's API (.start method) doesn't really let the user make actor systems with cyclic referential relationships like:

a -> b -> c -> a

where a -> b denotes that actor a holds a reference to actor b (probably for the better!)

However, I find this useful.

What is idiomatic Pykka for initialising such a ring of actors?

@jodal
Copy link
Owner

jodal commented Sep 17, 2013

I think I'd just send the last reference as a message to the actor needing it after they're started. You could also look up the last reference in the actor registry, though I tend to try to avoid using the registry since it is shared state.

Note that if you're using proxies and have a cyclic graph of actors, you can quickly fall into the trap of creating a deadlock if you regularly block on method calls between these actors. I've fallen into that pit myself, and had to use pykka.debug.log_thread_tracebacks() to get a clue about what was happening.

@fatuhoku
Copy link
Author

Good insight... I guess the 'references' I speak of need really be actor_refs. That way we are safe from deadlocks, though of course there's no guarantee whether the programmer will just get a proxy() object and start calling methods. I guess that is down to discipline.

Right now I create a bag of actor proxies and quickly start the actors in quick succession, assigning them fields of the bag. Thankfully, I'm only interacting with other actors while handling messages atm, but I can see an issue such as this:

class ActorSystem:
    pass

system = ActorSystem()
system.a = FooActor.start(system)
system.b = BarActor.start(system)
system.c = BazActor.start(system)
system.d = QuuxActor.start(system)

class FooActor:
    def __init__(self, system):
        # call super...
        self.system = system

    def on_start(self):
        self.system.b.tell({"Cool": "beans"}) # this would be a problem: this may execute before the assignment onto system.b will have happened

...
# similar declarations for b, c, d

Obviously initialisation order is horrible terrible here, but I don't see any other way with the current API.

This prompted me to type in "Dependency Injection Python" into Google. This doesn't bode well!

An idea: make the actor_refs future values?!?!?

@drozzy
Copy link

drozzy commented Sep 18, 2014

There is no reason you should be passing the whole system around.
What you are doing here is a Service Locator --- you have one giant global service registry and you are requesting actors from it. You are not saying that actor "FooActor" depends on actor "BarActor" --- instead you simply depend on the whole system -- so it can depend on ANY actor.
This is not dependency injection.

What are you trying to accomplish that requires circular references? Actor systems should be organized in supervision-hierarchies (something currently missing from pykka unfortunately):
http://doc.akka.io/docs/akka/snapshot/general/supervision.html

Here is an example of a hierarchy known as Error Kernel, where dangerous behavior is pushed down to the leaves:
http://stackoverflow.com/a/23113179/74865

If you need a ring of actors that pass messages to each other, consider using an intermediary "bus" actor, that will deliver the messages instead. Or, alternatively you can create a ring network -- in which case you need an actor that will actually DO the connecting, i.e.:

class SupervisingActor
    def on_start(self):
      node1, node2, node3, ... = NodeActor.start()
      node1.tell({'connect_left' : node3, 'connect_right': node2})
      node2.tell({'connect_left': node1, 'connect_right' : node3})
      # etc...
      node1.tell({'command': 'start!'})

Forgive the silly example.

@fatuhoku
Copy link
Author

I've since moved on from that particular project. You raise very valid points. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants