-
Notifications
You must be signed in to change notification settings - Fork 12
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
Actor state via messages #190
base: master
Are you sure you want to change the base?
Conversation
This is a draft of the `tractor` way to implement the example from the "processs pool" in the stdlib's `concurrent.futures` docs: https://docs.python.org/3/library/concurrent.futures.html#processpoolexecutor-example Our runtime is of course slower to startup but once up we of course get the same performance, this confirms that we need to focus some effort not on warm up and teardown times. The mp forkserver method definitely improves startup delay; rolling our own will likely be a good hot spot to play with. What's really nice is our implementation is done in approx 10th the code ;) Also, do we want offer and interface that yields results as they arrive? Relates to #175
c0dbcc5
to
ce61230
Compare
import trio | ||
import tractor | ||
|
||
_snd_chan, _recv_chan = trio.open_memory_channel(100) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry ignore this..
got left in from another idea.
import tractor | ||
|
||
_snd_chan, _recv_chan = trio.open_memory_channel(100) | ||
_actor_state = {'some_state_stuff': None} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a module level variable meaning it maintains it's state for the entire process lifetime.
We could have just as easily made some function that "sleeps forever" and wakes up incrementally to report it's state if need be.
|
||
|
||
@dataclass | ||
class MyProcessStateThing: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of this what are you after?
A function that creates some object and then makes that object mutateable from another inbound message?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you want something like ray
s "actors" (which i would argue aren't really "actor model" actors):
https://docs.ray.io/en/latest/actors.html
We can also accomplish this but it will require a slight bit more machinery.
# in a global var or are another clas scoped variable? | ||
# If you want it somehow persisted in another namespace | ||
# I'd be interested to know "where". | ||
actor = ActorState() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get this isn't ideal (though it really is no performance hit) in an idiomatic python sense, but the alternative is some other way to store this instance across function-task calls.
The normal way would be a module level variable (since they're "globally scoped") but I guess in theory you could have a function that stays alive and constantly passes the instance to other tasks over a memory channel - still in that case how does the new task get access to the channel handle?). The alternative is a module level class which has a class level variable which is again globally scoped on the class.
) | ||
|
||
|
||
def get_method_proxy(portal, target=ActorState) -> MethodProxy: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In cases anyone gets cranky about this, from pykka
docs:
The proxy object will use introspection to figure out what public attributes and methods the actor has, and then mirror the full API of the actor. Any attribute or method prefixed with underscore will be ignored, which is the convention for keeping stuff private in Python.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, there's been a lot of input from you in different channels, I'll summarize my thoughts here. I'm afraid I still don't see the functionality (or even ways to implement the functionality) I need. When I think of an actor system, these are some of things I'm worried about:
I can see how my API (in https://gist.github.com/fjarri/803d2611fc8487c7853f17ed4ad1ec10) allows one to implement this functionality. The framework will have enough information to handle these problems. Right now |
@fjarri thanks so much for the writeup 👍🏼. I'll address these in order:
async with tractor.wait_for_actor('parent_name') as portal:
... do stuff with portal... This is documented only very briefly. Note the "arbiter" stuff there is wrong and going away.
However it wants to. Restarts are a naive basic operation the exact same as spawning or running a task through a portal. If you want a special error that the parent expects to handle then you can just raise it and expect it on the parent's side or you could just have the result of the remote task return a restart message of your choosing. I've pushed up an example showing both multiple task(s) restarts within a single subactor as well as restarts of multiple subactors entirely themselves: To understand what's going more easily from a terminal you can run it with the following command:
I honestly have no idea what you're asking here so you'll have to clarify. There is currently no hot-reloading of code support yet mostly because python doesn't have great facilities for this and thus it needs some thinking but we've had lots of discussion.
Again don't know what you mean. If you want a reactive style delay there are already
As per the 1st bullet, contact the actor by name, get a portal, send it a message, get a response. |
# We'll show both. | ||
|
||
async with trio.open_nursery() as n: | ||
# we'll doe the first as a lone task restart in a daemon actor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
brutal typo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's clearly 3 tasks that are constantly restarted...
for i in range(4): | ||
n.start_soon(respawn_remote_task, p0) | ||
|
||
# Open another nursery that will respawn sub-actors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope x this. no extra nursery required.. originally it had that but we don't need it.
# Open another nursery that will respawn sub-actors | ||
|
||
# spawn a set of subactors that will signal restart | ||
# of the group of processes on each failures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still need to add a loop around the nursery to restart everything if we get a certain error raised. this starts getting into a more formal supervisor strategy api that we have yet to design.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm we might have to plug into the nursery internals a bit more to get granular control on each sub-proc. needs a little more pondering fo sho.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh and common strat names from erlang:
https://learnyousomeerlang.com/supervisors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually i guess you'd want to pass the nursery into the strat so that you get the granular control.. hmm that might compose nicely actually. Then in theory you could use a stack to compose multiple strats?
Woo this actually will be interesting i'm thinking.
ce61230
to
5ffd2d2
Compare
FYI we can probably vastly improve the example "proxy" code here with the new native bidir streaming support in #209 🏄🏼 |
An example for @fjarri doing "actor state mutation via messages".
Feel free to criticize the heck out of it.
If you want to see how subactors could modify each other's state we can add that as well.