import%20marimo%0A%0A__generated_with%20%3D%20%220.19.9%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Services%3A%20Managing%20Worker%20Pools%0A%0A%20%20%20%20In%20the%20last%20notebook%2C%20we%20saw%20how%20generation%20variance%20makes%20synchronous%20RL%0A%20%20%20%20wasteful%20%E2%80%94%20the%20trainer%20sits%20idle%20waiting%20for%20the%20slowest%20generator.%20The%20fix%0A%20%20%20%20is%20running%20many%20generators%20in%20parallel%2C%20decoupled%20from%20training.%0A%0A%20%20%20%20But%20managing%20a%20pool%20of%20workers%20brings%20its%20own%20problems.%20Let's%20see%20what%0A%20%20%20%20happens%20when%20you%20try%20to%20do%20it%20by%20hand.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20random%0A%20%20%20%20from%20monarch.actor%20import%20Actor%2C%20endpoint%2C%20current_rank%2C%20this_host%2C%20get_or_spawn_controller%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20Actor%2C%0A%20%20%20%20%20%20%20%20current_rank%2C%0A%20%20%20%20%20%20%20%20endpoint%2C%0A%20%20%20%20%20%20%20%20get_or_spawn_controller%2C%0A%20%20%20%20%20%20%20%20random%2C%0A%20%20%20%20%20%20%20%20this_host%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(Actor%2C%20current_rank%2C%20endpoint%2C%20random)%3A%0A%20%20%20%20class%20Worker(Actor)%3A%0A%20%20%20%20%20%20%20%20%22%22%22A%20worker%20that%20processes%20requests.%20May%20fail%20randomly.%22%22%22%0A%0A%20%20%20%20%20%20%20%20def%20__init__(self%2C%20fail_rate%3A%20float%20%3D%200.0)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.rank%20%3D%20current_rank().rank%0A%20%20%20%20%20%20%20%20%20%20%20%20self.fail_rate%20%3D%20fail_rate%0A%20%20%20%20%20%20%20%20%20%20%20%20self.calls%20%3D%200%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20ping(self)%20-%3E%20bool%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20True%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20process(self%2C%20data%3A%20str)%20-%3E%20str%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Process%20a%20request.%20Might%20fail%20based%20on%20fail_rate.%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20self.calls%20%2B%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20random.random()%20%3C%20self.fail_rate%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%20RuntimeError(f%22Worker%20failed!%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20f%22Processed%20'%7Bdata%7D'%22%0A%0A%20%20%20%20return%20(Worker%2C)%0A%0A%0A%40app.cell%0Adef%20_(Worker%2C%20this_host)%3A%0A%20%20%20%20%23%20%3D%3D%3D%20The%20Manual%20Approach%20%3D%3D%3D%0A%20%20%20%20host%20%3D%20this_host()%0A%20%20%20%20procs%20%3D%20host.spawn_procs(per_host%3D%7B%22procs%22%3A%204%7D)%0A%0A%20%20%20%20%23%20Spawn%204%20workers%20on%20individual%20proc%20slices%0A%20%20%20%20manual_workers%20%3D%20%5B%5D%0A%20%20%20%20for%20i%20in%20range(4)%3A%0A%20%20%20%20%20%20%20%20w%20%3D%20procs.slice(procs%3Di).spawn(f%22w_%7Bi%7D%22%2C%20Worker%2C%20fail_rate%3D0.3)%0A%20%20%20%20%20%20%20%20manual_workers.append(w)%0A%0A%20%20%20%20%23%20Manual%20round-robin%20with%20manual%20failure%20tracking%0A%20%20%20%20healthy%20%3D%20list(range(4))%0A%20%20%20%20current_idx%20%3D%200%0A%0A%20%20%20%20print(%22%3D%3D%3D%20Manual%20Worker%20Management%20%3D%3D%3D%22)%0A%20%20%20%20for%20req%20in%20range(6)%3A%0A%20%20%20%20%20%20%20%20if%20not%20healthy%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%20%20Request%20%7Breq%7D%3A%20No%20workers%20left!%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20break%0A%0A%20%20%20%20%20%20%20%20worker_idx%20%3D%20healthy%5Bcurrent_idx%20%25%20len(healthy)%5D%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20manual_workers%5Bworker_idx%5D.process.call_one(f%22req_%7Breq%7D%22).get()%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%20%20Request%20%7Breq%7D%3A%20Worker%20%7Bworker_idx%7D%20-%3E%20OK%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20current_idx%20%2B%3D%201%0A%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%20%20Request%20%7Breq%7D%3A%20Worker%20%7Bworker_idx%7D%20FAILED%20%E2%80%94%20removing%20from%20pool%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20healthy.remove(worker_idx)%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20But%20is%20it%20really%20dead%3F%20Or%20just%20a%20transient%20error%3F%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20How%20do%20we%20check%20later%3F%20When%20do%20we%20add%20it%20back%3F%0A%0A%20%20%20%20print(f%22%5Cn%7Blen(healthy)%7D%2F4%20workers%20still%20in%20pool%22)%0A%20%20%20%20print(%22Questions%3A%22)%0A%20%20%20%20print(%22%20%20Who%20checks%20if%20failed%20workers%20recovered%3F%22)%0A%20%20%20%20print(%22%20%20What%20if%20multiple%20clients%20need%20this%20same%20pool%3F%22)%0A%20%20%20%20print(%22%20%20What%20if%20we%20have%2050%20workers%20across%2010%20nodes%3F%22)%0A%20%20%20%20return%20(host%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20The%20Problems%20with%20Manual%20Management%0A%0A%20%20%20%20That%20works%20for%204%20workers%20in%20a%20notebook.%20But%20at%20scale%3A%0A%0A%20%20%20%20-%20**Shared%20state**%3A%20Multiple%20clients%20need%20the%20same%20pool%2C%20but%20each%20tracks%0A%20%20%20%20%20%20its%20own%20%60healthy%60%20list%20%E2%80%94%20when%20one%20client%20discovers%20a%20failure%2C%20others%0A%20%20%20%20%20%20don't%20know%0A%20%20%20%20-%20**Recovery**%3A%20No%20mechanism%20to%20check%20if%20failed%20workers%20come%20back%0A%20%20%20%20-%20**Discovery**%3A%20Clients%20must%20know%20exactly%20which%20workers%20exist%20at%20spawn%20time%0A%20%20%20%20-%20**Multi-GPU%20replicas**%3A%20A%2070B%20model%20needs%20tensor%20parallelism%20across%208%0A%20%20%20%20%20%20GPUs%20%E2%80%94%20each%20%22replica%22%20is%20a%20group%20of%20workers%2C%20not%20a%20single%20process%0A%0A%20%20%20%20The%20fix%3A%20centralize%20pool%20management%20in%20a%20**Service**%20actor.%20Since%20it's%20an%0A%20%20%20%20actor%2C%20its%20state%20(which%20replicas%20are%20healthy%2C%20the%20round-robin%20index)%20is%0A%20%20%20%20automatically%20shared%20across%20all%20callers%20%E2%80%94%20no%20duplicate%20tracking.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20The%20Service%20Actor%0A%0A%20%20%20%20The%20Service%20centralizes%20everything%20the%20manual%20approach%20did%20ad-hoc%3A%0A%0A%20%20%20%201.%20Takes%20a%20%60worker_class%60%20and%20a%20ProcMesh%2C%20slices%20it%20into%20replica-sized%0A%20%20%20%20%20%20%20chunks%2C%20and%20spawns%20an%20ActorMesh%20on%20each%0A%20%20%20%202.%20Routes%20requests%20round-robin%20across%20healthy%20replicas%0A%20%20%20%203.%20Tracks%20health%20%E2%80%94%20callers%20mark%20replicas%20unhealthy%2C%20the%20Service%20can%0A%20%20%20%20%20%20%20probe%20for%20recovery%0A%0A%20%20%20%20We%20treat%20**1%20replica%20%3D%201%20ActorMesh**%20%E2%80%94%20each%20replica%20can%20span%20multiple%0A%20%20%20%20GPUs%20for%20tensor%20parallelism.%20If%20your%20model%20fits%20on%20one%20GPU%2C%20a%20replica%20is%0A%20%20%20%20just%20size%201.%0A%0A%20%20%20%20The%20methods%20fall%20into%20three%20groups%3A%0A%0A%20%20%20%20-%20**Routing**%3A%20%60get_replica()%60%20%2F%20%60get_replica_with_idx()%60%20%E2%80%94%20round-robin%0A%20%20%20%20%20%20across%20healthy%20replicas%0A%20%20%20%20-%20**Health**%3A%20%60mark_unhealthy()%60%20%2F%20%60check_health()%60%20%E2%80%94%20callers%20report%0A%20%20%20%20%20%20failures%2C%20Service%20probes%20for%20recovery%0A%20%20%20%20-%20**Lifecycle**%3A%20%60__init__%60%20slices%20the%20ProcMesh%20and%20spawns%20workers%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Actor%2C%20endpoint)%3A%0A%20%20%20%20class%20Service(Actor)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Manages%20a%20pool%20of%20worker%20replicas%20with%20routing%20and%20health%20tracking.%0A%0A%20%20%20%20%20%20%20%20The%20Service%20owns%20worker%20lifecycle%3A%20it%20slices%20a%20ProcMesh%20into%0A%20%20%20%20%20%20%20%20replica-sized%20chunks%20and%20spawns%20an%20ActorMesh%20on%20each.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%0A%20%20%20%20%20%20%20%20def%20__init__(%0A%20%20%20%20%20%20%20%20%20%20%20%20self%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20service_name%3A%20str%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20worker_class%3A%20type%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20procs%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20procs_per_replica%3A%20int%20%3D%201%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20**worker_kwargs%2C%0A%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.service_name%20%3D%20service_name%0A%20%20%20%20%20%20%20%20%20%20%20%20total%20%3D%20len(procs)%0A%20%20%20%20%20%20%20%20%20%20%20%20self.num_replicas%20%3D%20total%20%2F%2F%20procs_per_replica%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Slice%20ProcMesh%20and%20spawn%20a%20replica%20on%20each%20chunk%0A%20%20%20%20%20%20%20%20%20%20%20%20self.replicas%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20i%20in%20range(self.num_replicas)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20start%20%3D%20i%20*%20procs_per_replica%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20end%20%3D%20start%20%2B%20procs_per_replica%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20replica_slice%20%3D%20procs.slice(procs%3Dslice(start%2C%20end))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20replica%20%3D%20replica_slice.spawn(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22replica_%7Bi%7D%22%2C%20worker_class%2C%20**worker_kwargs%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.replicas.append(replica)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.healthy%20%3D%20set(range(self.num_replicas))%0A%20%20%20%20%20%20%20%20%20%20%20%20self.unhealthy%3A%20set%5Bint%5D%20%3D%20set()%0A%20%20%20%20%20%20%20%20%20%20%20%20self.next_idx%20%3D%200%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%5BSERVICE%3A%7Bservice_name%7D%5D%20%7Bself.num_replicas%7D%20replicas%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22x%20%7Bprocs_per_replica%7D%20procs%20each%22)%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Routing%20---%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20get_replica(self)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Get%20a%20healthy%20replica%20(round-robin).%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20self.healthy%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%20RuntimeError(%22No%20healthy%20replicas%20available!%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20healthy_list%20%3D%20sorted(self.healthy)%0A%20%20%20%20%20%20%20%20%20%20%20%20idx%20%3D%20self.next_idx%20%25%20len(healthy_list)%0A%20%20%20%20%20%20%20%20%20%20%20%20self.next_idx%20%2B%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20self.replicas%5Bhealthy_list%5Bidx%5D%5D%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20get_replica_with_idx(self)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Get%20(replica%2C%20index).%20Use%20the%20index%20for%20mark_unhealthy().%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20self.healthy%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%20RuntimeError(%22No%20healthy%20replicas%20available!%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20healthy_list%20%3D%20sorted(self.healthy)%0A%20%20%20%20%20%20%20%20%20%20%20%20idx%20%3D%20self.next_idx%20%25%20len(healthy_list)%0A%20%20%20%20%20%20%20%20%20%20%20%20self.next_idx%20%2B%3D%201%0A%20%20%20%20%20%20%20%20%20%20%20%20replica_idx%20%3D%20healthy_list%5Bidx%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20self.replicas%5Breplica_idx%5D%2C%20replica_idx%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Health%20tracking%20---%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20mark_unhealthy(self%2C%20replica_idx%3A%20int)%20-%3E%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Remove%20a%20replica%20from%20rotation.%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20replica_idx%20in%20self.healthy%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.healthy.discard(replica_idx)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.unhealthy.add(replica_idx)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%5BSERVICE%3A%7Bself.service_name%7D%5D%20Replica%20%7Breplica_idx%7D%20unhealthy.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Healthy%3A%20%7Blen(self.healthy)%7D%2F%7Bself.num_replicas%7D%22)%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20mark_healthy(self%2C%20replica_idx%3A%20int)%20-%3E%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Reinstate%20a%20replica.%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20replica_idx%20%3C%20self.num_replicas%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.unhealthy.discard(replica_idx)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.healthy.add(replica_idx)%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20check_health(self)%20-%3E%20dict%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Probe%20unhealthy%20replicas.%20Reinstate%20those%20that%20respond%20to%20ping().%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20recovered%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20still_unhealthy%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20replica_idx%20in%20list(self.unhealthy)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Use%20.call()%20not%20.call_one()%20%E2%80%94%20works%20for%20multi-GPU%20replicas%20too%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.replicas%5Breplica_idx%5D.ping.call().get()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.unhealthy.discard(replica_idx)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.healthy.add(replica_idx)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20recovered.append(replica_idx)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%5BSERVICE%3A%7Bself.service_name%7D%5D%20Replica%20%7Breplica_idx%7D%20recovered!%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20still_unhealthy.append(replica_idx)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22recovered%22%3A%20recovered%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22still_unhealthy%22%3A%20still_unhealthy%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22healthy_count%22%3A%20len(self.healthy)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20get_health_status(self)%20-%3E%20dict%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22total%22%3A%20self.num_replicas%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22healthy%22%3A%20len(self.healthy)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22unhealthy%22%3A%20len(self.unhealthy)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22healthy_indices%22%3A%20sorted(self.healthy)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22unhealthy_indices%22%3A%20sorted(self.unhealthy)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Lifecycle%20---%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20get_all_replicas(self)%20-%3E%20list%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22Get%20all%20replicas%20(for%20operations%20that%20touch%20every%20replica).%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20list(self.replicas)%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20ping(self)%20-%3E%20bool%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20True%0A%0A%20%20%20%20return%20(Service%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Key%20design%20decisions%3A%0A%0A%20%20%20%20-%20**Service%20spawns%20workers**%20%E2%80%94%20it%20takes%20a%20%60worker_class%60%20and%20a%20ProcMesh%2C%0A%20%20%20%20%20%20slices%20it%20into%20replica-sized%20chunks%2C%20and%20spawns%20an%20ActorMesh%20on%20each.%0A%20%20%20%20%20%20Because%20the%20Service%20owns%20the%20lifecycle%2C%20it%20could%20also%20respawn%20failed%0A%20%20%20%20%20%20replicas.%0A%20%20%20%20-%20**Caller%20marks%20unhealthy**%20%E2%80%94%20the%20Service%20doesn't%20auto-detect%20failures.%0A%20%20%20%20%20%20The%20caller%20catches%20the%20exception%20and%20tells%20the%20Service.%20This%20is%20the%20same%0A%20%20%20%20%20%20try%2Fexcept%20pattern%20from%20notebook%2003.%0A%20%20%20%20-%20**Caller%20registers**%20%E2%80%94%20we%20call%20%60register_service()%60%20after%20spawning%0A%20%20%20%20%20%20because%20actors%20can't%20block%20on%20Futures%20during%20%60__init__%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Service%20Discovery%0A%0A%20%20%20%20Multiple%20parts%20of%20the%20system%20need%20the%20same%20services%20%E2%80%94%20the%20trainer%20needs%0A%20%20%20%20generators%2C%20the%20orchestrator%20needs%20the%20trainer.%20Rather%20than%20threading%0A%20%20%20%20references%20everywhere%2C%20we%20use%20a%20**singleton%20registry**.%20Monarch's%0A%20%20%20%20%60get_or_spawn_controller%60%20guarantees%20exactly%20one%20exists%20across%20all%0A%20%20%20%20processes.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Actor%2C%20endpoint%2C%20get_or_spawn_controller)%3A%0A%20%20%20%20class%20ServiceRegistry(Actor)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Singleton%20registry%20for%20service%20discovery.%22%22%22%0A%0A%20%20%20%20%20%20%20%20def%20__init__(self)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.services%3A%20dict%5Bstr%2C%20Actor%5D%20%3D%20%7B%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22%5BREGISTRY%5D%20ServiceRegistry%20spawned%22)%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20register(self%2C%20name%3A%20str%2C%20service)%20-%3E%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.services%5Bname%5D%20%3D%20service%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%5BREGISTRY%5D%20Registered%20'%7Bname%7D'%22)%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20get(self%2C%20name%3A%20str)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20name%20not%20in%20self.services%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%20KeyError(f%22Service%20'%7Bname%7D'%20not%20found%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20self.services%5Bname%5D%0A%0A%20%20%20%20%20%20%20%20%40endpoint%0A%20%20%20%20%20%20%20%20def%20list_services(self)%20-%3E%20list%5Bstr%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20list(self.services.keys())%0A%0A%0A%20%20%20%20def%20_get_registry()%3A%0A%20%20%20%20%20%20%20%20return%20get_or_spawn_controller(%22service_registry%22%2C%20ServiceRegistry).get()%0A%0A%20%20%20%20def%20get_service(name%3A%20str)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Get%20a%20service%20by%20name.%22%22%22%0A%20%20%20%20%20%20%20%20return%20_get_registry().get.call_one(name).get()%0A%0A%20%20%20%20def%20register_service(name%3A%20str%2C%20service)%20-%3E%20None%3A%0A%20%20%20%20%20%20%20%20%22%22%22Register%20a%20service%20for%20discovery.%22%22%22%0A%20%20%20%20%20%20%20%20_get_registry().register.call_one(name%2C%20service).get()%0A%0A%20%20%20%20def%20list_services()%20-%3E%20list%5Bstr%5D%3A%0A%20%20%20%20%20%20%20%20%22%22%22List%20all%20registered%20services.%22%22%22%0A%20%20%20%20%20%20%20%20return%20_get_registry().list_services.call_one().get()%0A%0A%20%20%20%20return%20get_service%2C%20list_services%2C%20register_service%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Seeing%20It%20Work%0A%0A%20%20%20%20First%2C%20the%20happy%20path%20%E2%80%94%204%20replicas%2C%20no%20failures.%20Watch%20the%20round-robin%0A%20%20%20%20distribute%20requests%20evenly.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Service%2C%20Worker%2C%20host%2C%20register_service)%3A%0A%20%20%20%20%23%20%3D%3D%3D%20Demo%201%3A%20Round-Robin%20Routing%20%3D%3D%3D%0A%20%20%20%20svc_proc%20%3D%20host.spawn_procs(per_host%3D%7B%22procs%22%3A%201%7D)%0A%20%20%20%20worker_procs_demo%20%3D%20host.spawn_procs(per_host%3D%7B%22procs%22%3A%204%7D)%0A%0A%20%20%20%20demo_svc%20%3D%20svc_proc.spawn(%0A%20%20%20%20%20%20%20%20%22demo_svc%22%2C%20Service%2C%0A%20%20%20%20%20%20%20%20service_name%3D%22demo%22%2C%0A%20%20%20%20%20%20%20%20worker_class%3DWorker%2C%0A%20%20%20%20%20%20%20%20procs%3Dworker_procs_demo%2C%0A%20%20%20%20%20%20%20%20procs_per_replica%3D1%2C%0A%20%20%20%20%20%20%20%20fail_rate%3D0.0%2C%0A%20%20%20%20)%0A%20%20%20%20register_service(%22demo%22%2C%20demo_svc)%0A%0A%20%20%20%20print(%22%3D%3D%3D%20Round-Robin%20Demo%20%3D%3D%3D%22)%0A%20%20%20%20for%20req_num%20in%20range(4)%3A%0A%20%20%20%20%20%20%20%20worker%2C%20idx%20%3D%20demo_svc.get_replica_with_idx.call_one().get()%0A%20%20%20%20%20%20%20%20response%20%3D%20worker.process.call_one(f%22req_%7Breq_num%7D%22).get()%0A%20%20%20%20%20%20%20%20print(f%22%20%20Request%20%7Breq_num%7D%3A%20replica%20%7Bidx%7D%20-%3E%20%7Bresponse%7D%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Handling%20Failures%0A%0A%20%20%20%20Now%20with%20a%2030%25%20failure%20rate.%20The%20pattern%3A%20try%2C%20catch%2C%20%60mark_unhealthy%60%2C%0A%20%20%20%20retry%20with%20a%20different%20replica.%20Then%20%60check_health%60%20to%20probe%20for%20recovery.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Service%2C%20Worker%2C%20host%2C%20register_service)%3A%0A%20%20%20%20%23%20%3D%3D%3D%20Demo%202%3A%20Failure%20Handling%20%2B%20Recovery%20%3D%3D%3D%0A%20%20%20%20flaky_svc_proc%20%3D%20host.spawn_procs(per_host%3D%7B%22procs%22%3A%201%7D)%0A%20%20%20%20flaky_worker_procs%20%3D%20host.spawn_procs(per_host%3D%7B%22procs%22%3A%204%7D)%0A%0A%20%20%20%20flaky_svc%20%3D%20flaky_svc_proc.spawn(%0A%20%20%20%20%20%20%20%20%22flaky_svc%22%2C%20Service%2C%0A%20%20%20%20%20%20%20%20service_name%3D%22flaky%22%2C%0A%20%20%20%20%20%20%20%20worker_class%3DWorker%2C%0A%20%20%20%20%20%20%20%20procs%3Dflaky_worker_procs%2C%0A%20%20%20%20%20%20%20%20procs_per_replica%3D1%2C%0A%20%20%20%20%20%20%20%20fail_rate%3D0.3%2C%0A%20%20%20%20)%0A%20%20%20%20register_service(%22flaky%22%2C%20flaky_svc)%0A%0A%20%20%20%20def%20process_with_retry(svc%2C%20data%2C%20max_retries%3D3)%3A%0A%20%20%20%20%20%20%20%20%22%22%22The%20canonical%20pattern%3A%20try%2Fexcept%20%2B%20mark_unhealthy%20%2B%20retry.%22%22%22%0A%20%20%20%20%20%20%20%20for%20attempt%20in%20range(max_retries)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20replica%2C%20idx%20%3D%20None%2C%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20replica%2C%20idx%20%3D%20svc.get_replica_with_idx.call_one().get()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20replica.process.call_one(data).get()%0A%20%20%20%20%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20idx%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%20%20%20%20Attempt%20%7Battempt%20%2B%201%7D%3A%20replica%20%7Bidx%7D%20failed%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20svc.mark_unhealthy.call_one(idx).get()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%20%20%20%20Attempt%20%7Battempt%20%2B%201%7D%3A%20no%20healthy%20replicas%22)%0A%20%20%20%20%20%20%20%20raise%20RuntimeError(%22All%20retries%20exhausted%22)%0A%0A%20%20%20%20print(%22%3D%3D%3D%20Failure%20Handling%20Demo%20%3D%3D%3D%22)%0A%20%20%20%20for%20j%20in%20range(5)%3A%0A%20%20%20%20%20%20%20%20print(f%22%20%20Request%20%7Bj%7D%3A%22)%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_result%20%3D%20process_with_retry(flaky_svc%2C%20f%22data_%7Bj%7D%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%20%20%20%20Success%3A%20%7B_result%7D%22)%0A%20%20%20%20%20%20%20%20except%20RuntimeError%20as%20e%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%20%20%20%20%7Be%7D%22)%0A%0A%20%20%20%20%23%20Check%20health%20after%20failures%0A%20%20%20%20status%20%3D%20flaky_svc.get_health_status.call_one().get()%0A%20%20%20%20print(f%22%5CnAfter%20failures%3A%20%7Bstatus%5B'healthy'%5D%7D%2F%7Bstatus%5B'total'%5D%7D%20healthy%22)%0A%20%20%20%20print(f%22Unhealthy%3A%20%7Bstatus%5B'unhealthy_indices'%5D%7D%22)%0A%0A%20%20%20%20%23%20Recover%20%E2%80%94%20workers%20are%20still%20alive%2C%20they%20just%20had%20transient%20errors%0A%20%20%20%20print(%22%5CnProbing%20unhealthy%20replicas...%22)%0A%20%20%20%20recovery%20%3D%20flaky_svc.check_health.call_one().get()%0A%20%20%20%20print(f%22Recovered%3A%20%7Brecovery%5B'recovered'%5D%7D%22)%0A%0A%20%20%20%20status_after%20%3D%20flaky_svc.get_health_status.call_one().get()%0A%20%20%20%20print(f%22Now%3A%20%7Bstatus_after%5B'healthy'%5D%7D%2F%7Bstatus_after%5B'total'%5D%7D%20healthy%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20Discovery%20in%20Action%0A%0A%20%20%20%20A%20completely%20separate%20part%20of%20the%20system%20can%20find%20services%20by%20name%20%E2%80%94%0A%20%20%20%20no%20need%20to%20pass%20actor%20references%20around.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(get_service%2C%20list_services)%3A%0A%20%20%20%20%23%20%3D%3D%3D%20Demo%203%3A%20Service%20Discovery%20%3D%3D%3D%0A%20%20%20%20print(%22%3D%3D%3D%20Service%20Discovery%20Demo%20%3D%3D%3D%22)%0A%20%20%20%20print(f%22Registered%20services%3A%20%7Blist_services()%7D%22)%0A%0A%20%20%20%20%23%20Find%20the%20demo%20service%20by%20name%20%E2%80%94%20no%20reference%20needed%0A%20%20%20%20discovered%20%3D%20get_service(%22demo%22)%0A%20%20%20%20found_worker%20%3D%20discovered.get_replica.call_one().get()%0A%20%20%20%20discovery_result%20%3D%20found_worker.process.call_one(%22found%20via%20discovery!%22).get()%0A%20%20%20%20print(f%22Discovered%20'demo'%20service%2C%20got%3A%20%7Bdiscovery_result%7D%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20What%20We%20Built%0A%0A%20%20%20%20%7C%20Component%20%7C%20Responsibility%20%7C%0A%20%20%20%20%7C-----------%7C---------------%7C%0A%20%20%20%20%7C%20**Worker**%20%7C%20Processes%20requests%2C%20has%20%60ping()%60%20for%20health%20checks%20%7C%0A%20%20%20%20%7C%20**Service**%20%7C%20Spawns%20replicas%2C%20routes%20round-robin%2C%20tracks%20health%20%7C%0A%20%20%20%20%7C%20**ServiceRegistry**%20%7C%20Singleton%20for%20discovery%20(%60get_service(%22name%22)%60)%20%7C%0A%0A%20%20%20%20The%20pattern%3A%0A%20%20%20%201.%20Allocate%20procs%20and%20spawn%20a%20Service%20(which%20spawns%20its%20own%20workers)%0A%20%20%20%202.%20Register%20the%20service%20so%20clients%20can%20discover%20it%20by%20name%0A%20%20%20%203.%20Clients%20get%20replicas%2C%20make%20calls%2C%20handle%20failures%20inline%0A%20%20%20%204.%20On%20failure%3A%20%60mark_unhealthy%60%20%2B%20retry%20with%20a%20different%20replica%0A%20%20%20%205.%20Periodically%3A%20%60check_health%60%20to%20recover%20transient%20failures%0A%0A%20%20%20%20This%20Service%20class%20lives%20in%20%60src%2Fmonarch_utils%2Fservices.py%60%20%E2%80%94%20in%20notebook%0A%20%20%20%2007%2C%20we'll%20import%20it%20and%20use%20it%20to%20manage%20real%20generator%20workers.%0A%0A%20%20%20%20%23%23%20What's%20Missing%3F%0A%0A%20%20%20%20The%20generators%20need%20**updated%20weights**%20after%20the%20trainer%20takes%20a%20gradient%0A%20%20%20%20step.%20Right%20now%20they'd%20use%20stale%20weights%20forever.%0A%0A%20%20%20%20How%20do%20we%20efficiently%20move%20hundreds%20of%20megabytes%20of%20model%20weights%20from%0A%20%20%20%20trainer%20to%20generators%20%E2%80%94%20without%20blocking%20training%3F%0A%0A%20%20%20%20**Next%3A%20RDMA%20Weight%20Sync**%20%E2%80%94%20using%20Monarch's%20RDMA%20primitives%20for%20direct%0A%20%20%20%20GPU-to-GPU%20weight%20transfer.%0A%0A%20%20%20%20---%0A%0A%20%20%20%20**Previous%3A**%20%5BNB05%20%E2%80%94%20RL%20Intro%5D(.%2F05_rl_intro.html)%20%C2%B7%20**Next%3A**%20%5BNB07%20%E2%80%94%20RDMA%20Weight%20Sync%5D(.%2F07_rdma_weight_sync.html)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
a93d39b5531068be2998fda24727720a