forked from goodboy/tractor
Add a super basic supervisor/restart example
parent
70c7e09831
commit
4a4a786763
|
@ -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