forked from goodboy/tractor
Add a super basic supervisor/restart example
parent
70c7e09831
commit
4a4a786763
examples/actors
|
@ -0,0 +1,86 @@
|
||||||
|
import trio
|
||||||
|
import tractor
|
||||||
|
|
||||||
|
|
||||||
|
class Restart(Exception):
|
||||||
|
"""Restart signal"""
|
||||||
|
|
||||||
|
|
||||||
|
async def sleep_then_restart():
|
||||||
|
actor = tractor.current_actor()
|
||||||
|
print(f'{actor.uid} starting up!')
|
||||||
|
await trio.sleep(0.5)
|
||||||
|
raise Restart('This is a restart signal')
|
||||||
|
|
||||||
|
|
||||||
|
async def signal_restart_whole_actor():
|
||||||
|
actor = tractor.current_actor()
|
||||||
|
print(f'{actor.uid} starting up!')
|
||||||
|
await trio.sleep(0.5)
|
||||||
|
return 'restart_me'
|
||||||
|
|
||||||
|
|
||||||
|
async def respawn_remote_task(portal):
|
||||||
|
# start a task in the actor at the other end
|
||||||
|
# of the provided portal, when it signals a restart,
|
||||||
|
# restart it..
|
||||||
|
|
||||||
|
# This is much more efficient then restarting the undlerying
|
||||||
|
# process over and over since the python interpreter runtime
|
||||||
|
# stays up and we just submit a new task to run (which
|
||||||
|
# is just the original one we submitted repeatedly.
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
await portal.run(sleep_then_restart)
|
||||||
|
except tractor.RemoteActorError as error:
|
||||||
|
if 'Restart' in str(error):
|
||||||
|
# respawn the actor task
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
async def supervisor():
|
||||||
|
|
||||||
|
async with tractor.open_nursery() as tn:
|
||||||
|
|
||||||
|
p0 = await tn.start_actor('task_restarter', enable_modules=[__name__])
|
||||||
|
|
||||||
|
# Yes, you can do this from multiple tasks on one actor
|
||||||
|
# or mulitple lone tasks in multiple subactors.
|
||||||
|
# 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
|
||||||
|
for i in range(4):
|
||||||
|
n.start_soon(respawn_remote_task, p0)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
portals = []
|
||||||
|
|
||||||
|
# start initial subactor set
|
||||||
|
for i in range(4):
|
||||||
|
p = await tn.run_in_actor(signal_restart_whole_actor)
|
||||||
|
portals.append(p)
|
||||||
|
|
||||||
|
# now wait on results and respawn actors
|
||||||
|
# that request it
|
||||||
|
while True:
|
||||||
|
|
||||||
|
for p in portals:
|
||||||
|
result = await p.result()
|
||||||
|
|
||||||
|
if result == 'restart_me':
|
||||||
|
print(f'restarting {p.channel.uid}')
|
||||||
|
await p.cancel_actor()
|
||||||
|
await trio.sleep(0.5)
|
||||||
|
p = await tn.run_in_actor(signal_restart_whole_actor)
|
||||||
|
portals.append(p)
|
||||||
|
|
||||||
|
# this will block indefinitely so user must
|
||||||
|
# cancel with ctrl-c
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
trio.run(supervisor)
|
Loading…
Reference in New Issue