返回 DeepSeek-Reasonix
generation.go
根目录 / internal / control / generation.go
1 package control
2
3 import (
4 "reasonix/internal/event"
5 "reasonix/internal/extension"
6 )
7
8 // RuntimePhase is the observable publish/drain phase for this controller.
9 type RuntimePhase string
10
11 const (
12 RuntimePhaseActive RuntimePhase = "Active"
13 RuntimePhaseDraining RuntimePhase = "Draining"
14 RuntimePhaseUnknown RuntimePhase = "Unknown"
15 )
16
17 // SetRuntimeGeneration binds turn admission to the extension PublishGate.
18 // Zero clears generation-based admission.
19 func (c *Controller) SetRuntimeGeneration(gen uint64) {
20 if c == nil {
21 return
22 }
23 c.mu.Lock()
24 c.runtimeGeneration = gen
25 c.mu.Unlock()
26 }
27
28 // RuntimeGeneration returns the generation this controller serves.
29 func (c *Controller) RuntimeGeneration() uint64 {
30 if c == nil {
31 return 0
32 }
33 c.mu.Lock()
34 defer c.mu.Unlock()
35 return c.runtimeGeneration
36 }
37
38 // RuntimeOwner returns the lifecycle owner for this controller lineage.
39 func (c *Controller) RuntimeOwner() *extension.RuntimeOwner {
40 if c == nil {
41 return runtimeOwnerOrDefault(nil)
42 }
43 return runtimeOwnerOrDefault(c.runtimeOwner)
44 }
45
46 func runtimeOwnerOrDefault(owner *extension.RuntimeOwner) *extension.RuntimeOwner {
47 return extension.RuntimeOwnerOrDefault(owner)
48 }
49
50 // RuntimePhase reports Active when this generation is published, Draining
51 // when superseded, Unknown when generation tracking is disabled.
52 func (c *Controller) RuntimePhase() RuntimePhase {
53 if c == nil {
54 return RuntimePhaseUnknown
55 }
56 gen := c.RuntimeGeneration()
57 if gen == 0 {
58 return RuntimePhaseUnknown
59 }
60 gate := c.RuntimeOwner().Gate
61 if gate.Published() == gen {
62 return RuntimePhaseActive
63 }
64 if gate.IsDraining(gen) || gate.IsStale(gen) {
65 return RuntimePhaseDraining
66 }
67 return RuntimePhaseUnknown
68 }
69
70 func (c *Controller) rejectDrainingGenerationLocked() bool {
71 gen := c.runtimeGeneration
72 if gen == 0 || c.RuntimeOwner().Gate.AdmitNewWork(gen) {
73 return false
74 }
75 extension.DefaultLifecycleMetrics.AdmissionRejected.Add(1)
76 return true
77 }
78
79 func (c *Controller) emitDrainingNotice() {
80 if c == nil {
81 return
82 }
83 c.sink.Emit(event.Event{Kind: event.Notice, Level: event.LevelWarn, Text: "input was not accepted: runtime is draining after rebuild — please resend"})
84 }
85
86 // LastResumeDecision returns the most recent DecideResume result from
87 // checkpoint open (zero value when never assessed).
88 func (c *Controller) LastResumeDecision() extension.ResumeDecision {
89 if c == nil {
90 return extension.ResumeDecision{}
91 }
92 c.mu.Lock()
93 defer c.mu.Unlock()
94 return c.lastResumeDecision
95 }
96
97 // AssessResume runs DecideResume for the controller's generation. Recovery
98 // and doctor use this to refuse claiming clean rollback after irreversible
99 // external work.
100 func (c *Controller) AssessResume() extension.ResumeDecision {
101 if c == nil {
102 return extension.ResumeDecision{AllowResume: true, CleanRollback: true}
103 }
104 gen := c.RuntimeGeneration()
105 if gen == 0 {
106 gen = c.RuntimeOwner().Gate.Published()
107 }
108 d := c.RuntimeOwner().DecideResume(gen)
109 c.mu.Lock()
110 c.lastResumeDecision = d
111 c.mu.Unlock()
112 return d
113 }
114
114 lines GO