Skip to content

feat(run): OS-enforced service teardown so a dead manager cannot orphan services #566

Description

@jongio

Problem

#555 reported that when azd app run --detach died, the services it had already started kept running and self-restarting, holding ports, with no handle to kill them.

PR #565 fixed the five causes of the manager dying and of it leaving no handle behind, which removes the reported trigger. It did not add OS-enforced teardown, so a manager that dies some other way (SIGKILL, power loss, an unhandled panic) can still orphan its services.

Why it was deferred

An architecture review of the obvious fix (attach a kill-on-close Job Object from the shared service.StartService path) found three blockers:

  1. Ownership is wrong at the shared layer. StartService is also used by the short-lived azd app start, which returns immediately by design. A job owned by that process would kill the services the moment the command exits. The same applies to MCP-started services, which would become tied to the MCP host's lifetime.
  2. Post-start attachment races. Wrapper processes such as npm and mise spawn grandchildren before an AssignProcessToJobObject call can land, so those grandchildren escape the job. Doing it correctly needs CREATE_SUSPENDED, then assign, then resume.
  3. Coverage gap. A Job Object does nothing for Aspire, containers, or Unix, so it is a partial answer that still needs a second mechanism.

Doing this properly means an explicitly owned process group threaded through orchestration, which is a new subsystem in the service start path used by every command. That did not belong in a bug-fix PR.

Proposed direction

  • Introduce an explicit service group owned by the orchestrator, not by StartService.
  • Only the long-lived run manager takes ownership; start and MCP opt out.
  • Windows: CREATE_SUSPENDED, then AssignProcessToJobObject, then ResumeThread, so wrapper grandchildren cannot escape.
  • Unix: process group plus killpg, building on the existing setsid isolation.
  • Decide explicitly what should happen for Aspire and container-hosted services.

Context

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestlane/soloSwarm lane: must run alone, conflicts with every other lane

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions