master
go 125 lines 3.27 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package chartengine
4
5 import (
6 "fmt"
7 "sync"
8
9 "github.com/netdata/netdata/go/plugins/pkg/metrix"
10 "github.com/netdata/netdata/go/plugins/plugin/framework/chartengine/internal/program"
11 "github.com/netdata/netdata/go/plugins/plugin/framework/charttpl"
12 )
13
14 // Engine is the chartengine runtime scaffold.
15 //
16 // Current scope:
17 // - owns compiled immutable program snapshots,
18 // - supports load/reload from decoded specs or YAML,
19 // - keeps previous compiled snapshot on failed reload.
20 // - owns planner runtime state (match index, route cache, materialized lifecycle).
21 type Engine struct {
22 mu sync.RWMutex
23 state engineState
24 }
25
26 // New creates an engine scaffold with optional configuration.
27 func New(opts ...Option) (*Engine, error) {
28 cfg, err := applyOptions(opts...)
29 if err != nil {
30 return nil, err
31 }
32
33 runtimeStore := cfg.runtimeStore
34 if !cfg.runtimeStoreSet {
35 runtimeStore = metrix.NewRuntimeStore()
36 }
37
38 return &Engine{
39 state: engineState{
40 cfg: cfg,
41 routeCache: newRouteCache(),
42 materialized: newMaterializedState(),
43 runtimeStore: runtimeStore,
44 runtimeStats: newRuntimeMetrics(runtimeStore),
45 log: cfg.log,
46 },
47 }, nil
48 }
49
50 // Load compiles and publishes a new immutable program revision.
51 func (e *Engine) Load(spec *charttpl.Spec, revision uint64) error {
52 if e == nil {
53 return fmt.Errorf("chartengine: nil engine")
54 }
55
56 compiled, err := Compile(spec, revision)
57 if err != nil {
58 e.logWarningf("chartengine load failed revision=%d: %v", revision, err)
59 return err
60 }
61
62 e.mu.RLock()
63 cfg := e.state.cfg
64 e.mu.RUnlock()
65
66 autogenPolicy, selectorPolicy, err := resolveEffectivePolicy(cfg, spec.Engine)
67 if err != nil {
68 e.logWarningf("chartengine load failed revision=%d: %v", revision, err)
69 return err
70 }
71
72 e.mu.Lock()
73 e.state.cfg.autogen = autogenPolicy
74 e.state.cfg.selector = selectorPolicy
75 e.state.cfg.autogenContextNamespace = spec.ContextNamespace
76 e.state.program = compiled
77 e.state.matchIndex = buildMatchIndex(compiled.Charts())
78 // Template revision change resets routing/materialization internals.
79 e.state.routeCache = newRouteCache()
80 e.state.materialized = newMaterializedState()
81 e.state.engineEpoch++
82 e.state.outstanding = 0
83 e.mu.Unlock()
84 e.logInfof("chartengine program loaded revision=%d charts=%d metrics=%d", revision, len(compiled.Charts()), len(compiled.MetricNames()))
85 return nil
86 }
87
88 // LoadYAML decodes chart-template YAML, compiles it, and publishes the program.
89 func (e *Engine) LoadYAML(data []byte, revision uint64) error {
90 spec, err := charttpl.DecodeYAML(data)
91 if err != nil {
92 return err
93 }
94 return e.Load(spec, revision)
95 }
96
97 // ResetMaterialized clears only materialized chart/dimension lifecycle state.
98 //
99 // It preserves the loaded program and other planner runtime state.
100 func (e *Engine) ResetMaterialized() {
101 if e == nil {
102 return
103 }
104 e.mu.Lock()
105 e.state.materialized = newMaterializedState()
106 e.state.engineEpoch++
107 e.state.outstanding = 0
108 e.mu.Unlock()
109 }
110
111 // program returns the latest compiled immutable program snapshot.
112 func (e *Engine) program() *program.Program {
113 if e == nil {
114 return nil
115 }
116 e.mu.RLock()
117 p := e.state.program
118 e.mu.RUnlock()
119 return p
120 }
121
122 // ready reports whether a compiled program is currently loaded.
123 func (e *Engine) ready() bool {
124 return e.program() != nil
125 }