forked from goodboy/tractor
87 lines
2.5 KiB
Python
87 lines
2.5 KiB
Python
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)
|