Skip to content

Commit cacb428

Browse files
feat: Exporter for Proxy (#2199)
* First edition * Exporter collects Proxy information. * Improve metrics * Dynamically adjust refresh time.
1 parent 9e26988 commit cacb428

22 files changed

Lines changed: 890 additions & 22 deletions

codis/cmd/proxy/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ Options:
193193
}
194194
defer s.Close()
195195

196+
proxy.RefreshPeriod.Set(config.MaxDelayRefreshTimeInterval.Int64())
197+
196198
log.Warnf("create proxy with config\n%s", config)
197199

198200
if s, ok := utils.Argument(d, "--pidfile"); ok {

codis/config/proxy.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,5 @@ metrics_report_statsd_server = ""
118118
metrics_report_statsd_period = "1s"
119119
metrics_report_statsd_prefix = ""
120120

121+
# Maximum delay statistical time interval.(This value must be greater than 0.)
122+
max_delay_refresh_time_interval = "15s"

codis/pkg/proxy/config.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ metrics_report_influxdb_database = ""
133133
metrics_report_statsd_server = ""
134134
metrics_report_statsd_period = "1s"
135135
metrics_report_statsd_prefix = ""
136+
137+
# Maximum delay statistical time interval.(This value must be greater than 0.)
138+
max_delay_refresh_time_interval = "15s"
136139
`
137140

138141
type Config struct {
@@ -192,6 +195,8 @@ type Config struct {
192195
MetricsReportStatsdPeriod timesize.Duration `toml:"metrics_report_statsd_period" json:"metrics_report_statsd_period"`
193196
MetricsReportStatsdPrefix string `toml:"metrics_report_statsd_prefix" json:"metrics_report_statsd_prefix"`
194197

198+
MaxDelayRefreshTimeInterval timesize.Duration `toml:"max_delay_refresh_time_interval" json:"max_delay_refresh_time_interval"`
199+
195200
ConfigFileName string `toml:"-" json:"config_file_name"`
196201
}
197202

@@ -323,5 +328,9 @@ func (c *Config) Validate() error {
323328
return errors.New("invalid metrics_report_statsd_period")
324329
}
325330

331+
if c.MaxDelayRefreshTimeInterval <= 0 {
332+
return errors.New("max_delay_refresh_time_interval must be greater than 0")
333+
}
334+
326335
return nil
327336
}

codis/pkg/proxy/proxy.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,12 @@ func (p *Proxy) ConfigGet(key string) *redis.Resp {
308308
redis.NewBulkBytes([]byte("metrics_report_statsd_prefix")),
309309
redis.NewBulkBytes([]byte(p.config.MetricsReportStatsdPrefix)),
310310
})
311+
case "max_delay_refresh_time_interval":
312+
if text, err := p.config.MaxDelayRefreshTimeInterval.MarshalText(); err != nil {
313+
return redis.NewErrorf("cant get max_delay_refresh_time_interval value.")
314+
} else {
315+
return redis.NewBulkBytes(text)
316+
}
311317
default:
312318
return redis.NewErrorf("unsupported key: %s", key)
313319
}
@@ -342,6 +348,18 @@ func (p *Proxy) ConfigSet(key, value string) *redis.Resp {
342348
}
343349
p.config.SlowlogLogSlowerThan = n
344350
return redis.NewString([]byte("OK"))
351+
case "max_delay_refresh_time_interval":
352+
s := &(p.config.MaxDelayRefreshTimeInterval)
353+
err := s.UnmarshalText([]byte(value))
354+
if err != nil {
355+
return redis.NewErrorf("err:%s.", err)
356+
}
357+
if d := p.config.MaxDelayRefreshTimeInterval.Duration(); d <= 0 {
358+
return redis.NewErrorf("max_delay_refresh_time_interval must be greater than 0")
359+
} else {
360+
RefreshPeriod.Set(int64(d))
361+
return redis.NewString([]byte("OK"))
362+
}
345363
default:
346364
return redis.NewErrorf("unsupported key: %s", key)
347365
}
@@ -558,7 +576,8 @@ type Stats struct {
558576
PrimaryOnly bool `json:"primary_only"`
559577
} `json:"backend"`
560578

561-
Runtime *RuntimeStats `json:"runtime,omitempty"`
579+
Runtime *RuntimeStats `json:"runtime,omitempty"`
580+
SlowCmdCount int64 `json:"slow_cmd_count"` // Cumulative count of slow log
562581
}
563582

564583
type RuntimeStats struct {
@@ -667,5 +686,6 @@ func (p *Proxy) Stats(flags StatsFlags) *Stats {
667686
stats.Runtime.NumCgoCall = runtime.NumCgoCall()
668687
stats.Runtime.MemOffheap = unsafe2.OffheapBytes()
669688
}
689+
stats.SlowCmdCount = SlowCmdCount.Int64()
670690
return stats
671691
}

codis/pkg/proxy/session.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,14 @@ func (s *Session) loopWriter(tasks *RequestChan) (err error) {
236236
} else {
237237
s.incrOpStats(r, resp.Type)
238238
}
239+
nowTime := time.Now().UnixNano()
240+
duration := int64((nowTime - r.ReceiveTime) / 1e3)
241+
s.updateMaxDelay(duration, r)
239242
if fflush {
240243
s.flushOpStats(false)
241244
}
242-
nowTime := time.Now().UnixNano()
243-
duration := int64((nowTime - r.ReceiveTime) / 1e3)
244245
if duration >= s.config.SlowlogLogSlowerThan {
246+
SlowCmdCount.Incr() // Atomic global variable, increment by 1 when slow log occurs.
245247
//client -> proxy -> server -> porxy -> client
246248
//Record the waiting time from receiving the request from the client to sending it to the backend server
247249
//the waiting time from sending the request to the backend server to receiving the response from the server
@@ -758,3 +760,10 @@ func (s *Session) handlePConfig(r *Request) error {
758760
}
759761
return nil
760762
}
763+
764+
func (s *Session) updateMaxDelay(duration int64, r *Request) {
765+
e := s.getOpStats(r.OpStr) // There is no race condition in the session
766+
if duration > e.maxDelay.Int64() {
767+
e.maxDelay.Set(duration)
768+
}
769+
}

codis/pkg/proxy/stats.go

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ import (
1414
"pika/codis/v2/pkg/utils/sync2/atomic2"
1515
)
1616

17+
var (
18+
SlowCmdCount atomic2.Int64 // Cumulative count of slow log
19+
RefreshPeriod atomic2.Int64
20+
)
21+
1722
type opStats struct {
1823
opstr string
1924
calls atomic2.Int64
@@ -22,14 +27,16 @@ type opStats struct {
2227
redis struct {
2328
errors atomic2.Int64
2429
}
30+
maxDelay atomic2.Int64
2531
}
2632

2733
func (s *opStats) OpStats() *OpStats {
2834
o := &OpStats{
29-
OpStr: s.opstr,
30-
Calls: s.calls.Int64(),
31-
Usecs: s.nsecs.Int64() / 1e3,
32-
Fails: s.fails.Int64(),
35+
OpStr: s.opstr,
36+
Calls: s.calls.Int64(),
37+
Usecs: s.nsecs.Int64() / 1e3,
38+
Fails: s.fails.Int64(),
39+
MaxDelay: s.maxDelay.Int64(),
3340
}
3441
if o.Calls != 0 {
3542
o.UsecsPercall = o.Usecs / o.Calls
@@ -45,6 +52,7 @@ type OpStats struct {
4552
UsecsPercall int64 `json:"usecs_percall"`
4653
Fails int64 `json:"fails"`
4754
RedisErrType int64 `json:"redis_errtype"`
55+
MaxDelay int64 `json:"max_delay"`
4856
}
4957

5058
var cmdstats struct {
@@ -62,6 +70,7 @@ var cmdstats struct {
6270

6371
func init() {
6472
cmdstats.opmap = make(map[string]*opStats, 128)
73+
SlowCmdCount.Set(0)
6574
go func() {
6675
for {
6776
start := time.Now()
@@ -72,6 +81,16 @@ func init() {
7281
cmdstats.qps.Set(int64(normalized + 0.5))
7382
}
7483
}()
84+
85+
// Clear the accumulated maximum delay to 0
86+
go func() {
87+
for {
88+
time.Sleep(time.Duration(RefreshPeriod.Int64()))
89+
for _, s := range cmdstats.opmap {
90+
s.maxDelay.Set(0)
91+
}
92+
}
93+
}()
7594
}
7695

7796
func OpTotal() int64 {
@@ -165,6 +184,22 @@ func incrOpStats(e *opStats) {
165184
s.redis.errors.Add(n)
166185
cmdstats.redis.errors.Add(n)
167186
}
187+
188+
/**
189+
Each session refreshes its own saved metrics, and there is a race condition at this time.
190+
Use the CAS method to update.
191+
*/
192+
for {
193+
oldValue := s.maxDelay
194+
if e.maxDelay > oldValue {
195+
if s.maxDelay.CompareAndSwap(oldValue.Int64(), e.maxDelay.Int64()) {
196+
e.maxDelay.Set(0)
197+
break
198+
}
199+
} else {
200+
break
201+
}
202+
}
168203
}
169204

170205
var sessions struct {

include/pika_server.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ class PikaServer : public pstd::noncopyable {
328328
uint32_t SlowlogLen();
329329
void SlowlogObtain(int64_t number, std::vector<SlowlogEntry>* slowlogs);
330330
void SlowlogPushEntry(const PikaCmdArgsType& argv, int64_t time, int64_t duration);
331+
uint64_t SlowlogCount();
331332

332333
/*
333334
* Statistic used
@@ -688,6 +689,7 @@ class PikaServer : public pstd::noncopyable {
688689
* Slowlog used
689690
*/
690691
uint64_t slowlog_entry_id_ = 0;
692+
uint64_t slowlog_counter_ = 0;
691693
std::shared_mutex slowlog_protector_;
692694
std::list<SlowlogEntry> slowlog_list_;
693695

src/pika_admin.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ void InfoCmd::InfoStats(std::string& info) {
10621062
tmp_stream << "is_slots_migrating:" << (is_migrating ? "Yes, " : "No, ") << start_migration_time_str << ", "
10631063
<< (is_migrating ? (current_time_s - start_migration_time) : (end_migration_time - start_migration_time))
10641064
<< "\r\n";
1065-
1065+
tmp_stream << "slow_logs_count:" << g_pika_server->SlowlogCount() << "\r\n";
10661066
info.append(tmp_stream.str());
10671067
}
10681068

src/pika_server.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,11 +1209,17 @@ void PikaServer::SlowlogPushEntry(const PikaCmdArgsType& argv, int64_t time, int
12091209
entry.start_time = time;
12101210
entry.duration = duration;
12111211
slowlog_list_.push_front(entry);
1212+
slowlog_counter_++;
12121213
}
12131214

12141215
SlowlogTrim();
12151216
}
12161217

1218+
uint64_t PikaServer::SlowlogCount() {
1219+
std::shared_lock l(slowlog_protector_);
1220+
return slowlog_counter_;
1221+
}
1222+
12171223
void PikaServer::ResetStat() {
12181224
statistic_.server_stat.accumulative_connections.store(0);
12191225
statistic_.server_stat.qps.querynum.store(0);

tools/pika_exporter/discovery/codis_dashboard.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,91 @@ type CodisModelInfo struct {
1616
Servers []CodisServerInfo `json:"servers"`
1717
}
1818

19+
type CodisProxyModelInfo struct {
20+
Id int `json:"id"`
21+
AdminAddr string `json:"admin_addr"`
22+
ProductName string `json:"product_name"`
23+
DataCenter string `json:"data_center"`
24+
}
25+
1926
type CodisGroupInfo struct {
2027
Models []CodisModelInfo `json:"models"`
2128
}
2229

30+
type CodisProxyInfo struct {
31+
Models []CodisProxyModelInfo `json:"models"`
32+
}
33+
2334
type CodisStatsInfo struct {
2435
Group CodisGroupInfo `json:"group"`
36+
Proxy CodisProxyInfo `json:"proxy"`
2537
}
2638

2739
type CodisTopomInfo struct {
2840
Stats CodisStatsInfo `json:"stats"`
2941
}
42+
43+
type RedisInfo struct {
44+
Errors int `json:"errors"`
45+
}
46+
47+
type CmdInfo struct {
48+
Opstr string `json:"opstr"`
49+
Calls int64 `json:"calls"`
50+
Usecs_percall int64 `json:"usecs_percall"`
51+
Fails int64 `json:"fails"`
52+
MaxDelay int64 `json:"max_delay"`
53+
}
54+
55+
type ProxyOpsInfo struct {
56+
Total int `json:"total"`
57+
Fails int `json:"fails"`
58+
Redis RedisInfo `json:"redis"`
59+
Qps int `json:"qps"`
60+
Cmd []CmdInfo `json:"cmd"`
61+
}
62+
63+
type RowInfo struct {
64+
Utime int64 `json:"utime"`
65+
Stime int64 `json:"stime"`
66+
MaxRss int64 `json:"max_rss"`
67+
IxRss int64 `json:"ix_rss"`
68+
IdRss int64 `json:"id_rss"`
69+
IsRss int64 `json:"is_rss"`
70+
}
71+
72+
type RusageInfo struct {
73+
Now string `json:"now"`
74+
Cpu float64 `json:"cpu"`
75+
Mem float64 `json:"mem"`
76+
Raw RowInfo `json:"raw"`
77+
}
78+
79+
type GeneralInfo struct {
80+
Alloc int64 `json:"alloc"`
81+
Sys int64 `json:"sys"`
82+
Lookups int64 `json:"lookups"`
83+
Mallocs int64 `json:"mallocs"`
84+
Frees int64 `json:"frees"`
85+
}
86+
87+
type HeapInfo struct {
88+
Alloc int64 `json:"alloc"`
89+
Sys int64 `json:"sys"`
90+
Idle int64 `json:"idle"`
91+
Inuse int64 `json:"inuse"`
92+
Objects int64 `json:"objects"`
93+
}
94+
95+
type RunTimeInfo struct {
96+
General GeneralInfo `json:"general"`
97+
Heap HeapInfo `json:"heap"`
98+
}
99+
100+
type ProxyStats struct {
101+
Online bool `json:"online"`
102+
Ops ProxyOpsInfo `json:"ops"`
103+
Rusage RusageInfo `json:"rusage"`
104+
RunTime RunTimeInfo `json:"runtime"`
105+
SlowCmdCount int64 `json:"slow_cmd_count"`
106+
}

0 commit comments

Comments
 (0)