runpod-lazy: no churn on unknown pod model; adopt existing pod on proxy restart

012e9fea1bfc457230dd4650ba1a3a0aed0e8107

permissionBRICK <40219477+permissionBRICK@users.noreply.github.com>

1 files changed, +19 -3Ignore whitespace
homelab/runpod-lazy-proxy.py+19 -3
@@ -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, None171 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):
357360
358def main():361def 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()