fix: RDP slot occupancy and cleanup_loop always running
- admin_page: slot shown as occupied based on ACTIVE status only (no time cutoff) - go_service: busy slots checked by ACTIVE status (no cutoff) — cleanup_loop handles expiry - startup_event: cleanup_loop starts regardless of ENABLE_STARTUP_MAINTENANCE flag; pool/container init guarded by the flag separately - cleanup_loop: RDPSLOT sessions expire correctly and trigger container restart Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+31
-31
@@ -1742,37 +1742,41 @@ def startup_event():
|
|||||||
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
|
fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
|
||||||
ensure_icons_dir()
|
ensure_icons_dir()
|
||||||
bootstrap_admin()
|
bootstrap_admin()
|
||||||
if not ENABLE_STARTUP_MAINTENANCE:
|
|
||||||
logger.info("startup_maintenance_disabled")
|
|
||||||
return
|
|
||||||
if not try_acquire_maintenance_leader():
|
if not try_acquire_maintenance_leader():
|
||||||
logger.info("maintenance_leader_skipped")
|
logger.info("maintenance_leader_skipped")
|
||||||
return
|
return
|
||||||
|
|
||||||
db = SessionLocal()
|
if ENABLE_STARTUP_MAINTENANCE:
|
||||||
try:
|
db = SessionLocal()
|
||||||
ensure_universal_pool()
|
try:
|
||||||
ensure_web_pool()
|
ensure_universal_pool()
|
||||||
for svc in db.scalars(
|
ensure_web_pool()
|
||||||
select(Service).where(
|
for svc in db.scalars(
|
||||||
Service.active == True,
|
select(Service).where(
|
||||||
Service.type.in_([ServiceType.WEB, ServiceType.RDP]),
|
Service.active == True,
|
||||||
)
|
Service.type.in_([ServiceType.WEB, ServiceType.RDP]),
|
||||||
).all():
|
)
|
||||||
if svc.type == ServiceType.WEB and WEB_POOL_SIZE <= 0:
|
).all():
|
||||||
ensure_warm_pool(svc)
|
if svc.type == ServiceType.WEB and WEB_POOL_SIZE <= 0:
|
||||||
elif svc.type == ServiceType.RDP:
|
ensure_warm_pool(svc)
|
||||||
slots = db.scalars(select(RdpSlot).where(RdpSlot.service_id == svc.id)).all()
|
elif svc.type == ServiceType.RDP:
|
||||||
for slot in slots:
|
slots = db.scalars(select(RdpSlot).where(RdpSlot.service_id == svc.id)).all()
|
||||||
try:
|
for slot in slots:
|
||||||
start_rdp_slot_container(slot, svc)
|
try:
|
||||||
slot.container_name = _rdp_slot_container_name(svc.slug, slot.id)
|
cname = _rdp_slot_container_name(svc.slug, slot.id)
|
||||||
except Exception:
|
try:
|
||||||
logger.exception("startup_rdp_slot_start_failed slot_id=%s", slot.id)
|
c = docker_client().containers.get(cname)
|
||||||
if slots:
|
if c.status != "running":
|
||||||
db.commit()
|
c.start()
|
||||||
finally:
|
except docker.errors.NotFound:
|
||||||
db.close()
|
start_rdp_slot_container(slot, svc)
|
||||||
|
slot.container_name = cname
|
||||||
|
except Exception:
|
||||||
|
logger.exception("startup_rdp_slot_start_failed slot_id=%s", slot.id)
|
||||||
|
if slots:
|
||||||
|
db.commit()
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
thread = threading.Thread(target=cleanup_loop, daemon=True)
|
thread = threading.Thread(target=cleanup_loop, daemon=True)
|
||||||
thread.start()
|
thread.start()
|
||||||
@@ -1953,7 +1957,6 @@ def admin_page(request: Request, admin: User = Depends(require_admin), db: Sessi
|
|||||||
{"cutoff": cutoff},
|
{"cutoff": cutoff},
|
||||||
).mappings().all()
|
).mappings().all()
|
||||||
rdp_slots: dict[int, list] = {}
|
rdp_slots: dict[int, list] = {}
|
||||||
cutoff_slot = now_utc() - dt.timedelta(seconds=SESSION_IDLE_SECONDS)
|
|
||||||
for svc in rdp_services:
|
for svc in rdp_services:
|
||||||
slots = db.scalars(select(RdpSlot).where(RdpSlot.service_id == svc.id).order_by(RdpSlot.id)).all()
|
slots = db.scalars(select(RdpSlot).where(RdpSlot.service_id == svc.id).order_by(RdpSlot.id)).all()
|
||||||
slot_list = []
|
slot_list = []
|
||||||
@@ -1962,7 +1965,6 @@ def admin_page(request: Request, admin: User = Depends(require_admin), db: Sessi
|
|||||||
select(SessionModel).where(
|
select(SessionModel).where(
|
||||||
SessionModel.container_id == f"RDPSLOT:{slot.id}",
|
SessionModel.container_id == f"RDPSLOT:{slot.id}",
|
||||||
SessionModel.status == SessionStatus.ACTIVE,
|
SessionModel.status == SessionStatus.ACTIVE,
|
||||||
SessionModel.last_access_at >= cutoff_slot,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
running = False
|
running = False
|
||||||
@@ -2162,12 +2164,10 @@ def go_service(
|
|||||||
session_id = str(uuid.uuid4())
|
session_id = str(uuid.uuid4())
|
||||||
try:
|
try:
|
||||||
with allocator_lock(db, 91003, timeout_seconds=GO_POOL_LOCK_TIMEOUT_SECONDS):
|
with allocator_lock(db, 91003, timeout_seconds=GO_POOL_LOCK_TIMEOUT_SECONDS):
|
||||||
cutoff = now_utc() - dt.timedelta(seconds=SESSION_IDLE_SECONDS)
|
|
||||||
busy_slot_ids: set[int] = set()
|
busy_slot_ids: set[int] = set()
|
||||||
for row in db.scalars(
|
for row in db.scalars(
|
||||||
select(SessionModel).where(
|
select(SessionModel).where(
|
||||||
SessionModel.status == SessionStatus.ACTIVE,
|
SessionModel.status == SessionStatus.ACTIVE,
|
||||||
SessionModel.last_access_at >= cutoff,
|
|
||||||
SessionModel.service_id == service.id,
|
SessionModel.service_id == service.id,
|
||||||
SessionModel.container_id.like("RDPSLOT:%"),
|
SessionModel.container_id.like("RDPSLOT:%"),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user