Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit b8fdc87

Browse files
committed
test: *astonishing sophisticated* system testing testcase, untested
misc: create .editorconfig to enforce code styles (especially imports sorting & grouping rules)
1 parent e60875f commit b8fdc87

File tree

7 files changed

+1091
-0
lines changed

7 files changed

+1091
-0
lines changed

.editorconfig

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
indent_size = 4
7+
indent_style = space
8+
insert_final_newline = false
9+
max_line_length = 120
10+
tab_width = 4
11+
ij_continuation_indent_size = 8
12+
ij_formatter_off_tag = @formatter:off
13+
ij_formatter_on_tag = @formatter:on
14+
ij_formatter_tags_enabled = false
15+
ij_smart_tabs = false
16+
ij_visual_guides = none
17+
ij_wrap_on_typing = false
18+
19+
[{*.go, *.go2}]
20+
indent_style = tab
21+
ij_continuation_indent_size = 4
22+
ij_go_GROUP_CURRENT_PROJECT_IMPORTS = true
23+
ij_go_add_leading_space_to_comments = true
24+
ij_go_add_parentheses_for_single_import = false
25+
ij_go_call_parameters_new_line_after_left_paren = true
26+
ij_go_call_parameters_right_paren_on_new_line = true
27+
ij_go_call_parameters_wrap = off
28+
ij_go_fill_paragraph_width = 80
29+
ij_go_group_stdlib_imports = true
30+
ij_go_import_sorting = gofmt
31+
ij_go_keep_indents_on_empty_lines = false
32+
ij_go_local_group_mode = project
33+
ij_go_move_all_imports_in_one_declaration = false
34+
ij_go_move_all_stdlib_imports_in_one_group = true
35+
ij_go_remove_redundant_import_aliases = false
36+
ij_go_use_back_quotes_for_imports = false
37+
ij_go_wrap_comp_lit = off
38+
ij_go_wrap_comp_lit_newline_after_lbrace = true
39+
ij_go_wrap_comp_lit_newline_before_rbrace = true
40+
ij_go_wrap_func_params = off
41+
ij_go_wrap_func_params_newline_after_lparen = true
42+
ij_go_wrap_func_params_newline_before_rparen = true
43+
ij_go_wrap_func_result = off
44+
ij_go_wrap_func_result_newline_after_lparen = true
45+
ij_go_wrap_func_result_newline_before_rparen = true

testcase/sdk-ticase-4885/go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module sdk-ticase-4885
2+
3+
go 1.16
4+
5+
require (
6+
github.com/pingcap/go-tpc v1.0.7 // indirect
7+
github.com/pingcap/test-infra/sdk v0.0.0-20210617124714-c7a66de592c8 // indirect
8+
github.com/prometheus/client_golang v1.11.0 // indirect
9+
)

testcase/sdk-ticase-4885/go.sum

Lines changed: 707 additions & 0 deletions
Large diffs are not rendered by default.

testcase/sdk-ticase-4885/main.go

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
. "net/url"
8+
"regexp"
9+
"strconv"
10+
"time"
11+
12+
"github.com/pingcap/test-infra/sdk/core"
13+
"github.com/pingcap/test-infra/sdk/resource"
14+
prometheus "github.com/prometheus/client_golang/api"
15+
prometheus_api "github.com/prometheus/client_golang/api/prometheus/v1"
16+
"github.com/prometheus/common/model"
17+
"go.uber.org/zap"
18+
)
19+
20+
func main() {
21+
ctx := try(core.BuildContext()).(core.TestContext)
22+
log := ctx.Logger()
23+
log.Info("start initializing environment...")
24+
25+
// Initialize param
26+
brUri := ctx.Param("tpcc-br-uri", "s3://tpcc/br-2t")
27+
tpccWarehouses, _ := strconv.Atoi(ctx.Param("tpcc-warehouses", "4"))
28+
tpccTables, _ := strconv.Atoi(ctx.Param("tpcc-tables", "4"))
29+
instanceType := ctx.Param("instance-type")
30+
scaleSize, _ := strconv.ParseUint(ctx.Param("scale-size"), 10, 32)
31+
scaleTimeout, _ := time.ParseDuration(ctx.Param("scale-timeout", "5m"))
32+
33+
// Initialize resource
34+
target := ctx.Resource("TiDBCluster").(resource.TiDBCluster)
35+
br := ctx.Resource("br").(resource.BR)
36+
tpc := ctx.Resource("go-tpc").(resource.WorkloadNode)
37+
38+
log.Info("environment initialized. running BR to import data.")
39+
err := exec("br", br, resource.WorkloadNodeExecOptions{
40+
Command: fmt.Sprintf("./br restore full --pd \"%s\" --storage \"%s\" --s3.endpoint http://minio.pingcap.net:9000 --send-credentials-to-tikv=true",
41+
regexp.MustCompile("^http(s)://").ReplaceAllString(try(target.ServiceURL(resource.PDAddr)).(string), ""),
42+
brUri),
43+
}, log)
44+
if err != nil {
45+
return
46+
}
47+
48+
log.Info("data imported. now running TPC-C test.")
49+
err = exec("go-tpc", tpc, resource.WorkloadNodeExecOptions{
50+
Command: fmt.Sprintf("./bin/go-tpc tpcc --warehouses %d run -T %d", tpccWarehouses, tpccTables),
51+
}, log)
52+
if err != nil {
53+
return
54+
}
55+
56+
stopper := make(chan int)
57+
go func() {
58+
// scale out
59+
scale := resource.TiDBClusterScaleOptions{
60+
Target: InstanceType(instanceType),
61+
Replicas: uint32(scaleSize), // TODO: get spec?
62+
Timeout: scaleTimeout,
63+
}
64+
log.Info("now scaling out.", zap.Any("scale_options", scale))
65+
err := target.Scale(scale)
66+
if err != nil {
67+
log.Error("errored when scaling out", zap.Any("scale_options", scale), zap.Error(err))
68+
}
69+
time.Sleep(3 * time.Hour)
70+
71+
// scale in
72+
scale = resource.TiDBClusterScaleOptions{
73+
Target: InstanceType(instanceType),
74+
Replicas: uint32(scaleSize), // TODO: get spec?
75+
Timeout: scaleTimeout,
76+
}
77+
log.Info("now scaling in.", zap.Any("scale_options", scale))
78+
err = target.Scale(scale)
79+
if err != nil {
80+
log.Error("errored when scaling in", zap.Any("scale_options", scale), zap.Error(err))
81+
}
82+
time.Sleep(3 * time.Hour)
83+
84+
upgrade := resource.TiDBClusterUpgradeOptions{
85+
Target: InstanceType(ctx.Param("upgrade-target")),
86+
Image: ctx.Param("upgrade-image"),
87+
}
88+
err = target.Upgrade(upgrade)
89+
if err != nil {
90+
log.Error("errored when upgrade", zap.Any("upgrade_options", upgrade), zap.Error(err))
91+
}
92+
time.Sleep(3 * time.Hour)
93+
94+
stopper <- 0
95+
}()
96+
97+
go func() {
98+
u := try(target.ServiceURL(resource.Prometheus)).(URL)
99+
url := u.String()
100+
client, err := prometheus.NewClient(prometheus.Config{
101+
Address: url,
102+
})
103+
if err != nil {
104+
log.Error("errored when create Prometheus client", zap.String("url", url), zap.Error(err))
105+
return
106+
}
107+
api := prometheus_api.NewAPI(client)
108+
109+
tidbClusters, err := queryParams("sum(pd_cluster_status) by (tidb_cluster)", "tidb_cluster", api, log)
110+
if err != nil {
111+
return
112+
}
113+
114+
// instances, err := queryParams("sum(pd_cluster_status) by (instance)", "instance", api, log)
115+
// if err != nil {
116+
// return
117+
// }
118+
119+
start := time.Now()
120+
INTERVAL := 5 * time.Second // flush and reevaluate constraints every INTERVAL time.
121+
122+
// QPS: sum(rate(tidb_executor_statement_total{tidb_cluster="testbed-tidbcluster-getstatus-jijbtjve-tc-ymcwmxhu"}[1m]))
123+
// Duration: histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket{tidb_cluster="testbed-tidbcluster-getstatus-jijbtjve-tc-ymcwmxhu"}[1m])) by (le))
124+
// Transaction OPS: sum(rate(tidb_session_transaction_duration_seconds_count{tidb_cluster="testbed-tidbcluster-getstatus-jijbtjve-tc-ymcwmxhu"}[1m])) by (type)
125+
// Up Store Count: sum(pd_cluster_status{tidb_cluster="testbed-tidbcluster-getstatus-jijbtjve-tc-ymcwmxhu", instance="tc-ymcwmxhu-pd-0", type="store_up_count"})
126+
127+
// 将 “TPS” 理解为 “QPS”
128+
qpsQuery := fmt.Sprintf("sum(rate(tidb_executor_statement_total{tidb_cluster=\"%s\"}[1m]))", *tidbClusters)
129+
// 将 “lat” 理解为 “Duration 的 80% 位百分数”
130+
durationQuery := fmt.Sprintf("histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket{tidb_cluster=\"$tidb_cluster\"}[1m])) by (le))")
131+
132+
for {
133+
select {
134+
case <-stopper:
135+
return
136+
default:
137+
log.Info("constraints reevaluating...")
138+
139+
// QPS
140+
log.Info("retrieving Query Summary -- QPS...")
141+
qpssValue, err := queryRange(qpsQuery,
142+
prometheus_api.Range{
143+
Start: start,
144+
End: time.Now(),
145+
Step: INTERVAL,
146+
}, api, log)
147+
if err != nil {
148+
return
149+
}
150+
qpssMatrix, err := expectMatrix(qpsQuery, qpssValue, log)
151+
if err != nil {
152+
return
153+
}
154+
155+
secondLast, latest := retrieveLastTwo("QPS", (*qpssMatrix[0]).Values, log)
156+
// 将 “TPS 不能下降到 三分之一 以下” 理解为 “最新一条记录 不能下降到 倒数第二条记录 的三分之一及以下”
157+
// 即 “倒数第二条记录 是 最新一条记录 的三倍及以上时,限制不满足”
158+
if secondLast >= 3.0*latest {
159+
log.Error("[CONSTRAINT VIOLATED] QOS constraint breached.")
160+
}
161+
162+
// Duration
163+
log.Info("retrieving Query Summary -- 80% Duration")
164+
durationValue, err := queryRange(durationQuery,
165+
prometheus_api.Range{
166+
Start: start,
167+
End: time.Now(),
168+
Step: INTERVAL,
169+
}, api, log)
170+
if err != nil {
171+
return
172+
}
173+
durationMatrix, err := expectMatrix(durationQuery, durationValue, log)
174+
if err != nil {
175+
return
176+
}
177+
178+
secondLast, latest = retrieveLastTwo("80% Duration", (*durationMatrix[0]).Values, log)
179+
// 将 “lat 不能升高 10% 以上” 理解为 “最新一条记录 不能升高到 倒数第二条记录 的百分之十及以上”
180+
// 即 “最新一条记录 是 倒数第二条记录 的 1.1 倍及以上时,限制不满足”
181+
if latest >= secondLast*1.1 {
182+
log.Error("[CONSTRAINT VIOLATED] 80% Duration constraint breached.")
183+
}
184+
}
185+
}
186+
}()
187+
}
188+
189+
func retrieveLastTwo(metric string, samplePairs []model.SamplePair, log *zap.Logger) (model.SampleValue, model.SampleValue) {
190+
secondLast := samplePairs[len(samplePairs)-2]
191+
latest := samplePairs[len(samplePairs)-1]
192+
log.Info(metric+" retrieved.", zap.Any("second_last_value", secondLast), zap.Any("latest_value", latest))
193+
return secondLast.Value, latest.Value
194+
}
195+
196+
func queryParams(q string, labelName model.LabelName, api prometheus_api.API, log *zap.Logger) (*model.LabelValue, error) {
197+
value, err := queryNow(q, api, log)
198+
if err != nil {
199+
return nil, err
200+
}
201+
vector, err := expectVector(q, value, log)
202+
if err != nil {
203+
return nil, err
204+
}
205+
206+
if len(vector) != 1 {
207+
msg := "a single element vector expected for given query, yet a vector whose length is not 1 returned"
208+
log.Error(msg,
209+
zap.String("query", q),
210+
zap.Any("vector", vector))
211+
return nil, errors.New(msg)
212+
} else {
213+
labelValue := vector[0].Metric[labelName]
214+
return &labelValue, nil
215+
}
216+
}
217+
218+
func expectVector(q string, value model.Value, log *zap.Logger) (model.Vector, error) {
219+
if value.Type() != model.ValVector {
220+
msg := "a vector expected for given query, but a non-Vector returned"
221+
log.Error(msg,
222+
zap.String("query", q),
223+
zap.Any("type", value.Type()),
224+
zap.String("value", value.String()))
225+
return nil, errors.New(msg)
226+
}
227+
return value.(model.Vector), nil
228+
}
229+
230+
func expectMatrix(q string, value model.Value, log *zap.Logger) (model.Matrix, error) {
231+
if value.Type() != model.ValMatrix {
232+
msg := "a matrix expected for given query, but a non-Vector returned"
233+
log.Error(msg,
234+
zap.String("query", q),
235+
zap.Any("type", value.Type()),
236+
zap.String("value", value.String()))
237+
return nil, errors.New(msg)
238+
}
239+
return value.(model.Matrix), nil
240+
}
241+
242+
func queryRange(query string, rg prometheus_api.Range, api prometheus_api.API, log *zap.Logger) (model.Value, error) {
243+
value, warnings, err := api.QueryRange(context.Background(), query, rg)
244+
if err != nil {
245+
log.Error("errored when executing query against Prometheus", zap.String("query", query), zap.Error(err))
246+
return nil, err
247+
}
248+
if len(warnings) > 0 {
249+
log.Warn("warning returned when executing query against Prometheus", zap.String("query", query), zap.Strings("warnings", warnings))
250+
}
251+
return value, nil
252+
}
253+
254+
func queryNow(query string, api prometheus_api.API, log *zap.Logger) (model.Value, error) {
255+
value, warnings, err := api.Query(context.Background(), query, time.Now())
256+
if err != nil {
257+
log.Error("errored when executing query against Prometheus", zap.String("query", query), zap.Error(err))
258+
return nil, err
259+
}
260+
if len(warnings) > 0 {
261+
log.Warn("warning returned when executing query against Prometheus", zap.String("query", query), zap.Strings("warnings", warnings))
262+
}
263+
return value, nil
264+
}
265+
266+
func exec(description string, workload resource.WorkloadNode, options resource.WorkloadNodeExecOptions, log *zap.Logger) error {
267+
_, stderr, exitCode, err := workload.Exec(options)
268+
if err != nil {
269+
log.Error("errored when executing command workload", zap.String("description", description), zap.Any("options", options), zap.Error(err))
270+
return err
271+
}
272+
if exitCode != 0 {
273+
// TODO: do log.Error writes into stderr?
274+
log.Error("errored returned from executed command",
275+
zap.String("description", description),
276+
zap.Any("options", options),
277+
zap.Int("exitCode", exitCode),
278+
zap.String("stderr", stderr))
279+
return errors.New("errored returned from executed command. description: " + description)
280+
}
281+
return nil
282+
}
283+
284+
func InstanceType(str string) resource.TiDBCompType {
285+
comp, err := resource.TiDB.FromString(str)
286+
if err != nil {
287+
core.Fail("unsupported parameter instance-type " + str)
288+
return -1
289+
}
290+
return comp
291+
}
292+
293+
func try(xs ...interface{}) interface{} {
294+
if len(xs) == 0 {
295+
return nil
296+
}
297+
298+
if err, ok := xs[len(xs)-1].(error); ok && err != nil {
299+
core.Fail(err.Error())
300+
}
301+
302+
return xs[0]
303+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# 80pct Duration
2+
http -p HBb :9090/api/v1/query_range query=="histogram_quantile(0.80, sum(rate(tidb_server_handle_query_duration_seconds_bucket{tidb_cluster=\"testbed-tidbcluster-prometheus-pnenkqwj-tc-mcwygwwl\"}[1m])) by (le))" start==1623949000 end==1623949500 step==5m
3+
# QPS
4+
http -p HBb :9090/api/v1/query_range query=="sum(rate(tidb_executor_statement_total{tidb_cluster=\"testbed-tidbcluster-prometheus-pnenkqwj-tc-mcwygwwl\"}[1m]))" start==1623952537 end==1623952907 step==10s

testcase/sdk-ticase-4885/sysbench

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
mysql-host=127.0.0.1
2+
mysql-port=4000
3+
mysql-user=root
4+
mysql-password=
5+
mysql-db=sbtest
6+
time=600
7+
threads=2
8+
report-interval=10
9+
db-driver=mysql

testcase/sdk-ticase-4885/testbed.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
generateName: testbed-
2+
items:
3+
- name: tc
4+
type: TIDB_CLUSTER
5+
spec:
6+
pd:
7+
replicas: 1
8+
image: pingcap/pd:v5.0.0
9+
tidb:
10+
replicas: 1
11+
image: pingcap/tidb:v5.0.0
12+
tikv:
13+
replicas: 1
14+
image: pingcap/tikv:v5.0.0

0 commit comments

Comments
 (0)