requires that sess.stateMu is held
(ctx context.Context, sess *daggerSession)
| 388 | |
| 389 | // requires that sess.stateMu is held |
| 390 | func (srv *Server) removeDaggerSession(ctx context.Context, sess *daggerSession) error { |
| 391 | slog := slog.With("session", sess.sessionID) |
| 392 | |
| 393 | slog.Info("removing session; stopping client services and flushing") |
| 394 | defer slog.Debug("session removed") |
| 395 | |
| 396 | // check if the local cache needs pruning after session is removed, prune if so |
| 397 | defer func() { |
| 398 | if srv.isShuttingDown() { |
| 399 | return |
| 400 | } |
| 401 | time.AfterFunc(time.Second, srv.throttledGC) |
| 402 | }() |
| 403 | |
| 404 | srv.daggerSessionsMu.Lock() |
| 405 | delete(srv.daggerSessions, sess.sessionID) |
| 406 | srv.daggerSessionsMu.Unlock() |
| 407 | |
| 408 | sess.state = sessionStateRemoved |
| 409 | sess.beginClosing() |
| 410 | |
| 411 | var errs error |
| 412 | |
| 413 | // in theory none of this should block very long, but add a safeguard just in case |
| 414 | ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 60*time.Second) |
| 415 | defer cancel() |
| 416 | |
| 417 | if err := sess.services.StopSessionServices(ctx, sess.sessionID); err != nil { |
| 418 | slog.Warn("error stopping services", "error", err) |
| 419 | errs = errors.Join(errs, fmt.Errorf("stop client services: %w", err)) |
| 420 | } |
| 421 | |
| 422 | slog.Debug("stopped services") |
| 423 | |
| 424 | if sess.resolver != nil { |
| 425 | errs = errors.Join(errs, sess.resolver.Close()) |
| 426 | sess.resolver = nil |
| 427 | } |
| 428 | |
| 429 | // release containers + buildkit solver/session state in parallel |
| 430 | |
| 431 | var releaseGroup errgroup.Group |
| 432 | sess.containersMu.Lock() |
| 433 | defer sess.containersMu.Unlock() |
| 434 | for ctr := range sess.containers { |
| 435 | if ctr != nil { |
| 436 | releaseGroup.Go(func() error { |
| 437 | return ctr.Release(ctx) |
| 438 | }) |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | // clients may be mutated under clientMu alone (e.g. getOrInitClient's |
| 443 | // failure cleanup deletes entries without holding stateMu), so snapshot |
| 444 | // under clientMu rather than iterating the live map below. |
| 445 | sess.clientMu.RLock() |
| 446 | clients := make([]*daggerClient, 0, len(sess.clients)) |
| 447 | for _, client := range sess.clients { |
no test coverage detected