master
go 132 lines 3.11 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package cockroachdb
4
5 import (
6 "context"
7 "database/sql"
8 "errors"
9 "fmt"
10 "sync"
11 "time"
12
13 "github.com/netdata/netdata/go/plugins/pkg/funcapi"
14 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
15 )
16
17 var errSQLDSNNotSet = errors.New("SQL DSN is not set")
18
19 // funcRouter routes method calls to appropriate function handlers.
20 // Owns shared SQL connection used by all function handlers.
21 type funcRouter struct {
22 collector *Collector // for config (Functions.DSN, logger)
23
24 // Shared SQL connection
25 db *sql.DB
26 dbMu sync.Mutex
27
28 handlers map[string]funcapi.MethodHandler
29 }
30
31 func newFuncRouter(c *Collector) *funcRouter {
32 r := &funcRouter{
33 collector: c,
34 handlers: make(map[string]funcapi.MethodHandler),
35 }
36 r.handlers[topQueriesMethodID] = newFuncTopQueries(r)
37 r.handlers[runningQueriesMethodID] = newFuncRunningQueries(r)
38 return r
39 }
40
41 // Compile-time interface check.
42 var _ funcapi.MethodHandler = (*funcRouter)(nil)
43
44 func (r *funcRouter) MethodParams(ctx context.Context, method string) ([]funcapi.ParamConfig, error) {
45 if h, ok := r.handlers[method]; ok {
46 return h.MethodParams(ctx, method)
47 }
48 return nil, fmt.Errorf("unknown method: %s", method)
49 }
50
51 func (r *funcRouter) Handle(ctx context.Context, method string, params funcapi.ResolvedParams) *funcapi.FunctionResponse {
52 if h, ok := r.handlers[method]; ok {
53 return h.Handle(ctx, method, params)
54 }
55 return funcapi.NotFoundResponse(method)
56 }
57
58 func (r *funcRouter) Cleanup(ctx context.Context) {
59 for _, h := range r.handlers {
60 h.Cleanup(ctx)
61 }
62 r.dbMu.Lock()
63 defer r.dbMu.Unlock()
64 if r.db != nil {
65 _ = r.db.Close()
66 r.db = nil
67 }
68 }
69
70 // ensureDB lazily initializes the SQL connection.
71 func (r *funcRouter) ensureDB(ctx context.Context) error {
72 r.dbMu.Lock()
73 defer r.dbMu.Unlock()
74
75 if r.db != nil {
76 return nil
77 }
78 if r.collector.Functions.DSN == "" {
79 return errSQLDSNNotSet
80 }
81
82 db, err := sql.Open("pgx", r.collector.Functions.DSN)
83 if err != nil {
84 return fmt.Errorf("error opening SQL connection: %w", err)
85 }
86 db.SetMaxOpenConns(1)
87 db.SetMaxIdleConns(1)
88 db.SetConnMaxLifetime(10 * time.Minute)
89
90 timeout := r.sqlTimeout()
91 pingCtx, cancel := context.WithTimeout(ctx, timeout)
92 defer cancel()
93 if err := db.PingContext(pingCtx); err != nil {
94 _ = db.Close()
95 return fmt.Errorf("error pinging SQL connection: %w", err)
96 }
97
98 setCtx, cancel := context.WithTimeout(ctx, timeout)
99 if _, err := db.ExecContext(setCtx, "SET allow_unsafe_internals = on"); err != nil {
100 r.collector.Debugf("unable to set allow_unsafe_internals: %v", err)
101 }
102 cancel()
103
104 r.db = db
105 return nil
106 }
107
108 func (r *funcRouter) sqlTimeout() time.Duration {
109 if r.collector.Timeout.Duration() > 0 {
110 return r.collector.Timeout.Duration()
111 }
112 return time.Second
113 }
114
115 func (r *funcRouter) topQueriesLimit() int {
116 return r.collector.topQueriesLimit()
117 }
118
119 func cockroachMethods() []funcapi.MethodConfig {
120 return []funcapi.MethodConfig{
121 topQueriesMethodConfig(),
122 runningQueriesMethodConfig(),
123 }
124 }
125
126 func cockroachFunctionHandler(job collectorapi.RuntimeJob) funcapi.MethodHandler {
127 c, ok := job.Collector().(*Collector)
128 if !ok {
129 return nil
130 }
131 return c.funcRouter
132 }