返回 DeepSeek-Reasonix
service_shutdown.go
根目录 / internal / session / service_shutdown.go
1 package session
2
3 import (
4 "context"
5 "errors"
6 )
7
8 // CloseAll releases every runtime this service still owns. A runtime a client
9 // is still bound to is reported, not torn down underneath that client.
10 func (s *Service) CloseAll(ctx context.Context) error {
11 return s.closeAll(ctx, false)
12 }
13
14 func (s *Service) closeAll(ctx context.Context, terminal bool) error {
15 if s == nil {
16 return nil
17 }
18 s.mu.Lock()
19 runtimes := make([]*Runtime, 0, len(s.active))
20 for _, runtime := range s.active {
21 runtimes = append(runtimes, runtime)
22 }
23 s.mu.Unlock()
24 var closeErr error
25 for _, runtime := range runtimes {
26 closeErr = errors.Join(closeErr, s.closeRuntime(ctx, runtime, "", terminal))
27 }
28 return closeErr
29 }
30
31 // Shutdown closes execution ownership and the query projection workers that
32 // share this service's persistence root. CloseAll intentionally remains the
33 // runtime-only primitive; hosts and short-lived import services must use this
34 // lifecycle boundary before releasing or removing the root directory.
35 //
36 // Because callers rely on that guarantee, Shutdown is terminal: a runtime a
37 // client never unbound is still closed, so its writer lease and recovery
38 // handles are released instead of surviving until process exit. The leaked
39 // binding is reported rather than swallowed.
40 func (s *Service) Shutdown(ctx context.Context) error {
41 if s == nil {
42 return nil
43 }
44 // Stop and join cold-query workers before closing live runtimes. Otherwise
45 // a worker can open a recovery projection after CloseAll collected its
46 // runtime set and leave the Bolt handle behind during Windows cleanup.
47 s.query.Close()
48 return s.closeAll(ctx, true)
49 }
50
50 lines GO