master
go 214 lines 5.47 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package chartengine
4
5 import (
6 "github.com/netdata/netdata/go/plugins/plugin/framework/chartengine/internal/program"
7 )
8
9 // materializedState tracks engine-owned chart lifecycle across successful cycles.
10 type materializedState struct {
11 charts map[string]*materializedChartState
12 }
13
14 // materializedChartState tracks one materialized chart instance.
15 type materializedChartState struct {
16 templateID string
17 meta program.ChartMeta
18 lifecycle program.LifecyclePolicy
19 lastSeenSuccessSeq uint64
20 dimensions map[string]*materializedDimensionState
21 orderedDims []string
22 orderedDimsDirty bool
23 scratchEntries map[string]*dimBuildEntry
24 }
25
26 // materializedDimensionState tracks one materialized dimension in a chart.
27 type materializedDimensionState struct {
28 hidden bool
29 float bool
30 static bool
31 order int
32 algorithm program.Algorithm
33 multiplier int
34 divisor int
35 lastSeenSuccessSeq uint64
36 }
37
38 func newMaterializedState() materializedState {
39 return materializedState{
40 charts: make(map[string]*materializedChartState),
41 }
42 }
43
44 func (s materializedState) clone() materializedState {
45 if len(s.charts) == 0 {
46 return newMaterializedState()
47 }
48
49 out := materializedState{
50 charts: make(map[string]*materializedChartState, len(s.charts)),
51 }
52 for chartID, chart := range s.charts {
53 if chart == nil {
54 continue
55 }
56 out.charts[chartID] = chart.clone()
57 }
58 return out
59 }
60
61 func (c *materializedChartState) clone() *materializedChartState {
62 if c == nil {
63 return nil
64 }
65
66 out := &materializedChartState{
67 templateID: c.templateID,
68 meta: c.meta,
69 lifecycle: c.lifecycle,
70 lastSeenSuccessSeq: c.lastSeenSuccessSeq,
71 orderedDims: append([]string(nil), c.orderedDims...),
72 orderedDimsDirty: c.orderedDimsDirty,
73 }
74 if len(c.dimensions) > 0 {
75 out.dimensions = make(map[string]*materializedDimensionState, len(c.dimensions))
76 for name, dim := range c.dimensions {
77 if dim == nil {
78 continue
79 }
80 cloned := *dim
81 out.dimensions[name] = &cloned
82 }
83 } else {
84 out.dimensions = make(map[string]*materializedDimensionState)
85 }
86 if len(c.scratchEntries) > 0 {
87 out.scratchEntries = make(map[string]*dimBuildEntry, len(c.scratchEntries))
88 for name, entry := range c.scratchEntries {
89 if entry == nil {
90 continue
91 }
92 cloned := *entry
93 out.scratchEntries[name] = &cloned
94 }
95 }
96 return out
97 }
98
99 func (s *materializedState) ensureChart(
100 chartID string,
101 templateID string,
102 meta program.ChartMeta,
103 lifecycle program.LifecyclePolicy,
104 ) (*materializedChartState, bool) {
105 chart, ok := s.charts[chartID]
106 if ok {
107 if chart.templateID != templateID {
108 chart.templateID = templateID
109 chart.dimensions = make(map[string]*materializedDimensionState)
110 chart.orderedDims = nil
111 chart.orderedDimsDirty = false
112 chart.scratchEntries = nil
113 }
114 chart.meta = meta
115 chart.lifecycle = lifecycle
116 return chart, false
117 }
118 chart = &materializedChartState{
119 templateID: templateID,
120 meta: meta,
121 lifecycle: lifecycle,
122 dimensions: make(map[string]*materializedDimensionState),
123 }
124 s.charts[chartID] = chart
125 return chart, true
126 }
127
128 func (c *materializedChartState) ensureDimension(name string, state dimensionState) (*materializedDimensionState, bool) {
129 dim, ok := c.dimensions[name]
130 if ok {
131 if dim.static != state.static || dim.order != state.order {
132 c.orderedDimsDirty = true
133 }
134 dim.hidden = state.hidden
135 dim.float = state.float
136 dim.static = state.static
137 dim.order = state.order
138 dim.algorithm = state.algorithm
139 dim.multiplier = state.multiplier
140 dim.divisor = state.divisor
141 return dim, false
142 }
143 dim = &materializedDimensionState{
144 hidden: state.hidden,
145 float: state.float,
146 static: state.static,
147 order: state.order,
148 algorithm: state.algorithm,
149 multiplier: state.multiplier,
150 divisor: state.divisor,
151 }
152 c.dimensions[name] = dim
153 c.orderedDimsDirty = true
154 return dim, true
155 }
156
157 func (c *materializedChartState) removeDimension(name string) {
158 if _, ok := c.dimensions[name]; !ok {
159 return
160 }
161 delete(c.dimensions, name)
162 c.orderedDimsDirty = true
163 }
164
165 func (c *materializedChartState) orderedDimensionNames() []string {
166 if !c.orderedDimsDirty && len(c.orderedDims) == len(c.dimensions) {
167 return c.orderedDims
168 }
169 c.orderedDims = orderedMaterializedDimensionNames(c.dimensions)
170 c.orderedDimsDirty = false
171 return c.orderedDims
172 }
173
174 func (c *materializedChartState) checkoutScratchEntries(dimCap int) map[string]*dimBuildEntry {
175 if c.scratchEntries != nil {
176 return c.scratchEntries
177 }
178 c.scratchEntries = make(map[string]*dimBuildEntry, dimCap)
179 return c.scratchEntries
180 }
181
182 func (c *materializedChartState) storeScratchEntries(entries map[string]*dimBuildEntry) {
183 c.scratchEntries = entries
184 }
185
186 func (c *materializedChartState) pruneScratchEntries(currentSeq uint64) {
187 if len(c.scratchEntries) == 0 {
188 return
189 }
190 for name, entry := range c.scratchEntries {
191 if entry == nil {
192 delete(c.scratchEntries, name)
193 continue
194 }
195 if entry.seenSeq == currentSeq {
196 continue
197 }
198 if _, keep := c.dimensions[name]; keep {
199 continue
200 }
201 delete(c.scratchEntries, name)
202 }
203 }
204
205 func shouldExpire(lastSeenSuccessSeq, currentSuccessSeq uint64, expireAfterCycles int) bool {
206 if expireAfterCycles <= 0 {
207 return false
208 }
209 if lastSeenSuccessSeq == 0 || currentSuccessSeq <= lastSeenSuccessSeq {
210 return false
211 }
212 missedCycles := currentSuccessSeq - lastSeenSuccessSeq
213 return missedCycles >= uint64(expireAfterCycles)
214 }