返回 DeepSeek-Reasonix
model_application_retry.go
根目录 / internal / serve / model_application_retry.go
1 package serve
2
3 import (
4 "context"
5 "errors"
6 "log/slog"
7 "reasonix/internal/control"
8 "reasonix/internal/secrets"
9 "sync"
10 "time"
11 )
12
13 // One event-driven retry owner per foreground binding. A failed construction
14 // is not requeued; the next explicit retry or saved revision initiates it.
15 type modelApplicationRetry struct {
16 mu sync.Mutex
17 owner control.SessionAPI
18 pending, running, dirty bool
19 failedRevision string
20 failure string
21 }
22
23 func (s *Server) cachedModelApplicationFailureLocked(ctx context.Context) error {
24 r := &s.modelApplicationRetry
25 owner := s.ctl()
26 r.mu.Lock()
27 revision, message, previous := r.failedRevision, r.failure, r.owner
28 r.mu.Unlock()
29 if previous != owner || revision == "" {
30 return nil
31 }
32 d := s.modelApplicationDetailsLocked(ctx)
33 if d != nil && d.DesiredRevision == revision {
34 return errors.New(message)
35 }
36 return nil
37 }
38
39 func (s *Server) recordModelApplicationFailureLocked(ctx context.Context, err error) {
40 var failure *modelConstructionError
41 if !errors.As(err, &failure) {
42 return
43 }
44 owner := s.ctl()
45 r := &s.modelApplicationRetry
46 r.mu.Lock()
47 r.owner, r.failedRevision, r.failure = owner, failure.revision, secrets.RedactCredentials(err.Error())
48 r.mu.Unlock()
49 slog.Warn("model configuration application failed", "phase", "build")
50 }
51
52 type modelConstructionError struct {
53 cause error
54 revision string
55 }
56
57 func (e *modelConstructionError) Error() string { return e.cause.Error() }
58 func (e *modelConstructionError) Unwrap() error { return e.cause }
59
60 func (s *Server) modelConstructionFailure(err error) error {
61 revision := ""
62 if settings := s.managedModels; settings != nil && settings.SourceToken != "" {
63 revision = settings.Revision
64 } else if c, ok := s.ctl().(*control.Controller); ok {
65 _, revision, _ = c.ModelSettingsState()
66 }
67 return &modelConstructionError{cause: err, revision: revision}
68 }
69
70 func (s *Server) deferModelApplicationLocked() {
71 r := &s.modelApplicationRetry
72 owner := s.ctl()
73 r.mu.Lock()
74 r.owner = owner
75 r.pending = true
76 r.failedRevision = ""
77 r.failure = ""
78 r.mu.Unlock()
79 s.kickModelApplication()
80 }
81
82 func (s *Server) kickModelApplication() {
83 r := &s.modelApplicationRetry
84 r.mu.Lock()
85 if !r.pending {
86 r.mu.Unlock()
87 return
88 }
89 r.dirty = true
90 if r.running {
91 r.mu.Unlock()
92 return
93 }
94 r.running = true
95 r.mu.Unlock()
96 go func() {
97 for {
98 s.bindMu.Lock()
99 r.mu.Lock()
100 owner := r.owner
101 r.dirty = false
102 r.mu.Unlock()
103 if s.ctl() != owner || !control.ModelReplacementBlocked(owner) {
104 r.mu.Lock()
105 r.pending = false
106 r.mu.Unlock()
107 if s.ctl() == owner {
108 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
109 _ = s.refreshRunModelSettingsLocked(ctx)
110 cancel()
111 }
112 }
113 s.bindMu.Unlock()
114 r.mu.Lock()
115 again := r.pending && r.dirty
116 if !again {
117 r.running = false
118 }
119 r.mu.Unlock()
120 if !again {
121 return
122 }
123 }
124 }()
125 }
126
126 lines GO