-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
Copy pathsql_stats.go
191 lines (169 loc) · 5.89 KB
/
sql_stats.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Copyright 2021 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package sslocal
import (
"context"
"math"
"time"
"github.com/cockroachdb/cockroach/pkg/settings"
"github.com/cockroachdb/cockroach/pkg/settings/cluster"
"github.com/cockroachdb/cockroach/pkg/sql/sqlstats"
"github.com/cockroachdb/cockroach/pkg/sql/sqlstats/insights"
"github.com/cockroachdb/cockroach/pkg/sql/sqlstats/ssmemstorage"
"github.com/cockroachdb/cockroach/pkg/util/metric"
"github.com/cockroachdb/cockroach/pkg/util/mon"
"github.com/cockroachdb/cockroach/pkg/util/syncutil"
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
)
// SQLStats carries per-application in-memory statistics for all applications.
type SQLStats struct {
st *cluster.Settings
mu struct {
syncutil.Mutex
mon *mon.BytesMonitor
// lastReset is the last time at which the app containers were reset.
lastReset time.Time
// apps is the container for all the per-application statistics objects.
apps map[string]*ssmemstorage.Container
}
// Server level counter
atomic *ssmemstorage.SQLStatsAtomicCounters
// flushTarget is a Sink that, when the SQLStats resets at the end of its
// reset interval, the SQLStats will dump all of the stats into if it is not
// nil.
flushTarget Sink
knobs *sqlstats.TestingKnobs
insights insights.WriterProvider
latencyInformation insights.LatencyInformation
}
func newSQLStats(
st *cluster.Settings,
uniqueStmtFingerprintLimit *settings.IntSetting,
uniqueTxnFingerprintLimit *settings.IntSetting,
curMemBytesCount *metric.Gauge,
maxMemBytesHist metric.IHistogram,
insightsWriter insights.WriterProvider,
parentMon *mon.BytesMonitor,
flushTarget Sink,
knobs *sqlstats.TestingKnobs,
latencyInformation insights.LatencyInformation,
) *SQLStats {
monitor := mon.NewMonitor(
"SQLStats",
mon.MemoryResource,
curMemBytesCount,
maxMemBytesHist,
-1, /* increment */
math.MaxInt64,
st,
)
s := &SQLStats{
st: st,
flushTarget: flushTarget,
knobs: knobs,
insights: insightsWriter,
latencyInformation: latencyInformation,
}
s.atomic = ssmemstorage.NewSQLStatsAtomicCounters(
st,
uniqueStmtFingerprintLimit,
uniqueTxnFingerprintLimit)
s.mu.apps = make(map[string]*ssmemstorage.Container)
s.mu.mon = monitor
s.mu.mon.StartNoReserved(context.Background(), parentMon)
return s
}
// GetTotalFingerprintCount returns total number of unique statement and
// transaction fingerprints stored in the current SQLStats.
func (s *SQLStats) GetTotalFingerprintCount() int64 {
return s.atomic.GetTotalFingerprintCount()
}
// GetTotalFingerprintBytes returns the total amount of bytes currently
// allocated for storing statistics for both statement and transaction
// fingerprints.
func (s *SQLStats) GetTotalFingerprintBytes() int64 {
s.mu.Lock()
defer s.mu.Unlock()
return s.mu.mon.AllocBytes()
}
func (s *SQLStats) getStatsForApplication(appName string) *ssmemstorage.Container {
s.mu.Lock()
defer s.mu.Unlock()
if a, ok := s.mu.apps[appName]; ok {
return a
}
a := ssmemstorage.New(
s.st,
s.atomic,
s.mu.mon,
appName,
s.knobs,
s.insights(false /* internal */),
s.latencyInformation,
)
s.mu.apps[appName] = a
return a
}
// resetAndMaybeDumpStats clears all the stored per-app, per-statement and
// per-transaction statistics. If target is not nil, then the stats in s will be
// flushed into target.
func (s *SQLStats) resetAndMaybeDumpStats(ctx context.Context, target Sink) (err error) {
// Note: we do not clear the entire s.mu.apps map here. We would need
// to do so to prevent problems with a runaway client running `SET
// APPLICATION_NAME=...` with a different name every time. However,
// any ongoing open client session at the time of the reset has
// cached a pointer to its appStats struct and would thus continue
// to report its stats in an object now invisible to the target tools
// (virtual table, marshaling, etc.). It's a judgement call, but
// for now we prefer to see more data and thus not clear the map, at
// the risk of seeing the map grow unboundedly with the number of
// different application_names seen so far.
s.mu.Lock()
defer s.mu.Unlock()
// Clear the per-apps maps manually,
// because any SQL session currently open has cached the
// pointer to its appStats object and will continue to
// accumulate data using that until it closes (or changes its
// application_name).
for appName, statsContainer := range s.mu.apps {
lastErr := s.MaybeDumpStatsToLog(ctx, appName, statsContainer, target)
statsContainer.Clear(ctx)
err = lastErr
}
s.mu.lastReset = timeutil.Now()
return err
}
// MaybeDumpStatsToLog flushes stats into target If it is not nil.
func (s *SQLStats) MaybeDumpStatsToLog(
ctx context.Context, appName string, container *ssmemstorage.Container, target Sink,
) (err error) {
// Save the existing data to logs.
// TODO(knz/dt): instead of dumping the stats to the log, save
// them in a SQL table so they can be inspected by the DBA and/or
// the UI.
if sqlstats.DumpStmtStatsToLogBeforeReset.Get(&s.st.SV) {
container.SaveToLog(ctx, appName)
}
if target != nil {
lastErr := target.AddAppStats(ctx, appName, container)
// If we run out of memory budget, Container.Add() will merge stats in
// statsContainer with all the existing stats. However it will discard
// rest of the stats in statsContainer that requires memory allocation.
// We do not wish to short circuit here because we want to still try our
// best to merge all the stats that we can.
if lastErr != nil {
err = lastErr
}
}
return err
}
func (s *SQLStats) GetClusterSettings() *cluster.Settings {
return s.st
}