runpod-lazy: no churn on unknown pod model; adopt existing pod on proxy restart
| @@ -163,7 +163,9 @@ def ensure_pod(value=None, wait=True): | |||
| 163 | pod_id, have, gpu = (state['pod_id'], state['model'], state['gpu']) | 163 | pod_id, have, gpu = (state['pod_id'], state['model'], state['gpu']) |
| 164 | if not pod_id: | 164 | if not pod_id: |
| 165 | pod_id, have, gpu = find_pod() | 165 | pod_id, have, gpu = find_pod() |
| 166 | if pod_id and value and have != value: | 166 | # Only recreate when the pod's model is KNOWN and different. An unknown |
| 167 | # model (e.g. env not reported yet) must not churn pods. | ||
| 168 | if pod_id and value and have and have != value: | ||
| 167 | log(f'pod has model={have}, need {value} - recreating') | 169 | log(f'pod has model={have}, need {value} - recreating') |
| 168 | terminate(pod_id) | 170 | terminate(pod_id) |
| 169 | pod_id, have, gpu = None, None, None | 171 | pod_id, have, gpu = None, None, None |
| @@ -305,8 +307,9 @@ class Proxy(BaseHTTPRequestHandler): | |||
| 305 | catalog = {'models': payload.get('models', []), 'active': payload.get('active')} | 307 | catalog = {'models': payload.get('models', []), 'active': payload.get('active')} |
| 306 | save_catalog(catalog) | 308 | save_catalog(catalog) |
| 307 | log(f"catalog updated: {len(catalog['models'])} models, active={catalog['active']}") | 309 | log(f"catalog updated: {len(catalog['models'])} models, active={catalog['active']}") |
| 308 | # Re-provision proactively when the active model changed under a live pod. | 310 | # Re-provision proactively when the active model changed under a live pod |
| 309 | if state['pod_id'] and catalog['active'] and state['model'] != catalog['active']: | 311 | # (known-model pods only - never churn a pod of unknown provenance). |
| 312 | if state['pod_id'] and catalog['active'] and state['model'] and state['model'] != catalog['active']: | ||
| 310 | log('active model changed - re-provisioning pod') | 313 | log('active model changed - re-provisioning pod') |
| 311 | threading.Thread(target=self._safe_ensure, args=(catalog['active'],), daemon=True).start() | 314 | threading.Thread(target=self._safe_ensure, args=(catalog['active'],), daemon=True).start() |
| 312 | return self._reply(200, status_body()) | 315 | return self._reply(200, status_body()) |
| @@ -357,6 +360,19 @@ class Proxy(BaseHTTPRequestHandler): | |||
| 357 | 360 | ||
| 358 | def main(): | 361 | def main(): |
| 359 | threading.Thread(target=idle_reaper, daemon=True).start() | 362 | threading.Thread(target=idle_reaper, daemon=True).start() |
| 363 | # Adopt a pre-existing pod (e.g. after a proxy restart mid-provisioning) so | ||
| 364 | # its readiness wait - which also feeds the idle timer - keeps running. | ||
| 365 | pod_id, have, gpu = find_pod() | ||
| 366 | if pod_id: | ||
| 367 | state.update({'pod_id': pod_id, 'model': have, 'gpu': gpu, 'phase': 'orange', 'last': time.monotonic()}) | ||
| 368 | log(f'adopting existing pod {pod_id} (model={have})') | ||
| 369 | |||
| 370 | def _adopt(): | ||
| 371 | try: | ||
| 372 | ensure_pod(have) | ||
| 373 | except Exception as err: | ||
| 374 | log('adoption wait failed:', err) | ||
| 375 | threading.Thread(target=_adopt, daemon=True).start() | ||
| 360 | server = ThreadingHTTPServer(('0.0.0.0', LISTEN_PORT), Proxy) | 376 | server = ThreadingHTTPServer(('0.0.0.0', LISTEN_PORT), Proxy) |
| 361 | log(f'runpod-lazy v3 (catalog) on :{LISTEN_PORT} (pod "{POD_NAME}", DCs {DATACENTERS or "any"}, idle {IDLE_SECONDS}s)') | 377 | log(f'runpod-lazy v3 (catalog) on :{LISTEN_PORT} (pod "{POD_NAME}", DCs {DATACENTERS or "any"}, idle {IDLE_SECONDS}s)') |
| 362 | server.serve_forever() | 378 | server.serve_forever() |