Compare commits

...

3 Commits

Author SHA1 Message Date
Jiayuan Zhang
a49e114608 fix(daemon): use runtime's owner_id for agent migration, not caller's
The migration was gated on ownerID.Valid which is only true for PAT/JWT
registrations. Daemon token registrations (the common case for background
daemon restarts) had ownerID as zero, skipping migration entirely.

Fix: use registered.OwnerID (preserved via COALESCE on upsert) instead
of the caller's ownerID. This ensures migration runs even when the daemon
re-registers via daemon token after an upgrade.
2026-04-14 13:40:54 +08:00
Jiayuan Zhang
f7ef98358a fix(daemon): address code review issues on PR #906
1. Move gcRuntimes() to the main sweep loop — previously it was inside
   sweepStaleRuntimes() after an early return, so it only ran when new
   runtimes were marked stale. Now it runs every sweep cycle independently.

2. Fix DeleteStaleOfflineRuntimes to exclude runtimes with ANY agent
   reference (not just active ones). The FK agent.runtime_id is ON DELETE
   RESTRICT, so archived agents also block deletion.

3. Scope MigrateAgentsToRuntime to the same machine by matching
   daemon_id LIKE '<current_daemon_id>-%'. This prevents cross-machine
   agent migration when the same user has multiple devices.
2026-04-14 13:40:03 +08:00
Jiayuan Zhang
13518df8e5 fix(daemon): prevent duplicate runtime registration on profile switch
The daemon_id included a profile name suffix (e.g. "hostname-staging"),
so switching profiles created a new daemon_id that bypassed the UPSERT
dedup constraint, leaving orphaned runtime records in the database.

Three changes:
- Remove profile suffix from daemon_id — use stable hostname only.
  The unique constraint (workspace_id, daemon_id, provider) already
  prevents collisions within the same workspace.
- Auto-migrate agents from old offline runtimes to the newly registered
  runtime during DaemonRegister (same workspace/provider/owner).
- Add TTL-based GC in the runtime sweeper to delete offline runtimes
  with no active agents after 7 days.

Closes MUL-695
2026-04-14 13:40:03 +08:00

View File

@@ -219,15 +219,17 @@ func (h *Handler) DaemonRegister(w http.ResponseWriter, r *http.Request) {
}
// Migrate agents from old offline runtimes on the same machine to the
// newly registered runtime. Scoped by daemon_id prefix so that only
// old profile-suffixed runtimes (e.g. "hostname-staging") from this
// machine are affected — runtimes from other machines are untouched.
if ownerID.Valid {
// newly registered runtime. Uses the runtime's owner_id (preserved via
// COALESCE on upsert) so migration works with both PAT and daemon tokens.
// Scoped by daemon_id prefix so that only old profile-suffixed runtimes
// (e.g. "hostname-staging") from this machine are affected.
effectiveOwnerID := registered.OwnerID
if effectiveOwnerID.Valid {
migrated, err := h.Queries.MigrateAgentsToRuntime(r.Context(), db.MigrateAgentsToRuntimeParams{
NewRuntimeID: registered.ID,
WorkspaceID: parseUUID(req.WorkspaceID),
Provider: provider,
OwnerID: ownerID,
OwnerID: effectiveOwnerID,
DaemonIDPrefix: strToText(req.DaemonID),
})
if err != nil {