diff --git a/README.md b/README.md index 72291aa4509..15d52f5943e 100644 --- a/README.md +++ b/README.md @@ -378,6 +378,30 @@ Add release notes manually on GitHub. See the parsing bug and an upper bound before Click 9. + + [Serve] Keep reused replicas routable on count-only updates + 7f89638
(plus 6444f96, 82fc2fb) + sky/serve/replica_managers.py + + A sky serve update that changes only the + service: section (e.g. replicas: 1 → 2) + reuses running replicas by relabelling them to the new version, but + active_versions was only refreshed by the next prober + pass. Until then the load balancer filtered out every reused replica + and returned 503 No ready replicas for up to one sync + interval (20 s). Regression from upstream #3249, which replaced the + per-sync version choice used when reuse (#3214) was written. + Fix: recompute active_versions right after the relabel, + using the prober's own function.
+ Follow-up 6444f96: the prober wrote back its stale + ReplicaInfo snapshot after probing, reverting a relabel + that landed mid-probe (update_version does not hold the + replica lock), so the autoscaler replaced the healthy replica. The + prober now re-reads the stored version before writing back. + 82fc2fb removes the unit test added in + 6444f96. + + @@ -461,6 +485,8 @@ git cherry-pick 5420c46 bb8cbc9 0e986b2 f579438 # Intel xe GPU discovery and fo git cherry-pick b43391c 64919e5 # Publish wheels on push with unique versions git cherry-pick 44d09ea # Allow Click 8.2.1+ while excluding 8.2.0 and 9.x git cherry-pick 12bd32c # Mark wheel releases as latest (not prereleases) +# Keep reused replicas routable on count-only serve updates (3 commits, in order): +git cherry-pick 7f89638 6444f96 82fc2fb # Resolve any conflicts if upstream changed the same files # 4. Push new branch diff --git a/sky/serve/replica_managers.py b/sky/serve/replica_managers.py index 7fd96069315..8a9a9ac36a2 100644 --- a/sky/serve/replica_managers.py +++ b/sky/serve/replica_managers.py @@ -1422,6 +1422,13 @@ def _probe_all_replicas(self) -> None: f'seconds ({current_delay_seconds}s ' f'/ {initial_delay_seconds}s). ' 'Skipping.') + # `update_version` relabels replicas without holding the lock, + # possibly while this probe was in flight. Keep its version + # instead of writing back the stale one from our snapshot. + latest_info = serve_state.get_replica_info_from_id( + self._service_name, info.replica_id) + if latest_info is not None: + info.version = latest_info.version serve_state.add_or_update_replica(self._service_name, info.replica_id, info) if should_teardown: @@ -1539,6 +1546,13 @@ def update_version(self, version: int, spec: 'service_spec.SkyServiceSpec', logger.info('Replica config changed (rest), skipping. ' f'old: {old_config}, ' f'new: {new_config}') + # A reused replica now carries the new version, but `active_versions` + # still lists only the old ones until the next prober cycle, so the + # load balancer would drop every reused replica in between. Refresh it + # now so the reuse never takes a ready replica out of rotation. + serve_utils.set_service_status_and_active_versions_from_replica( + self._service_name, + serve_state.get_replica_infos(self._service_name), update_mode) def _get_version_spec(self, version: int) -> 'service_spec.SkyServiceSpec': spec = serve_state.get_spec(self._service_name, version)