Skip to content

Commit 223c294

Browse files
Merge pull request #11 from redis-performance/rps.consumer
Enabling non blocking XREADGROUP. tracking message/rate and command/rate
2 parents 1e3484e + 1db9506 commit 223c294

File tree

5 files changed

+91
-34
lines changed

5 files changed

+91
-34
lines changed

cmd/common.go

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,25 +98,29 @@ func updateCLI(tick *time.Ticker, c chan os.Signal, message_limit uint64, loop b
9898
}
9999

100100
var currentErr uint64 = 0
101-
var currentCount uint64 = 0
101+
var currentCommandCount uint64 = 0
102+
var currentMessageCount uint64 = 0
103+
prevCommandCount := uint64(0)
104+
prevMessageCount := uint64(0)
102105
start := time.Now()
103106
prevTime := time.Now()
104-
prevMessageCount := uint64(0)
107+
105108
messageRateTs := []float64{}
106109
cmdRateTs := map[string][]int{}
107110
cmdRateTick := make([]int, len(ALL_COMMANDS), len(ALL_COMMANDS))
108111
percentilesTs := []map[string]float64{}
109112
var dp datapoint
110-
fmt.Printf("%26s %7s %25s %25s %7s %25s %25s\n", "Test time", " ", "Total Commands", "Total Errors", "", "Command Rate", "p50 lat. (msec)")
113+
fmt.Printf("%26s %7s %25s %25s %25s %7s %25s %25s %25s\n", "Test time", " ", "Total Commands", "Total Messages", "Total Errors", "", "Command Rate", "Message Rate", "p50 lat. (msec)")
111114
for {
112115
select {
113116
case dp = <-datapointsChan:
114117
{
118+
currentCommandCount++
115119
// in case of blocking commands that did not processed entries we don't really account it
116120
if dp.processedEntries > 0 {
117121
latencies.RecordValue(dp.durationMs)
118122
latenciesTick.RecordValue(dp.durationMs)
119-
currentCount++
123+
currentMessageCount += uint64(dp.processedEntries)
120124
for _, cmdType := range dp.commandsIssued {
121125
cmdRateTick[cmdType]++
122126
}
@@ -129,17 +133,20 @@ func updateCLI(tick *time.Ticker, c chan os.Signal, message_limit uint64, loop b
129133
}
130134
case <-tick.C:
131135
{
132-
totalCommands += currentCount
136+
totalCommands += currentCommandCount
137+
totalMessages += currentMessageCount
133138
totalErrors += currentErr
134139
currentErr = 0
135-
currentCount = 0
140+
currentCommandCount = 0
141+
currentMessageCount = 0
136142
now := time.Now()
137143
took := now.Sub(prevTime)
138-
messageRate := float64(totalCommands-prevMessageCount) / float64(took.Seconds())
144+
commandRate := float64(totalCommands-prevCommandCount) / float64(took.Seconds())
145+
messageRate := float64(totalMessages-prevMessageCount) / float64(took.Seconds())
139146
completionPercentStr := "[----%]"
140147
errorPercentStr := "[----%]"
141148
if !loop {
142-
completionPercent := float64(totalCommands) / float64(message_limit) * 100.0
149+
completionPercent := float64(totalMessages) / float64(message_limit) * 100.0
143150
completionPercentStr = fmt.Sprintf("[%3.1f%%]", completionPercent)
144151
}
145152
if totalCommands > 0 {
@@ -149,10 +156,10 @@ func updateCLI(tick *time.Ticker, c chan os.Signal, message_limit uint64, loop b
149156

150157
p50 := float64(latencies.ValueAtQuantile(50.0)) / 1000.0
151158

152-
if prevMessageCount == 0 && totalCommands != 0 {
159+
if prevMessageCount == 0 && totalMessages != 0 {
153160
start = time.Now()
154161
}
155-
if totalCommands != 0 {
162+
if totalMessages != 0 {
156163
messageRateTs = append(messageRateTs, messageRate)
157164
_, perTickLatencies := generateLatenciesMap(latenciesTick, took)
158165
percentilesTs = append(percentilesTs, perTickLatencies)
@@ -166,22 +173,23 @@ func updateCLI(tick *time.Ticker, c chan os.Signal, message_limit uint64, loop b
166173
cmdRateTick[pos] = 0
167174
}
168175

169-
prevMessageCount = totalCommands
176+
prevMessageCount = totalMessages
177+
prevCommandCount = totalCommands
170178
prevTime = now
171179

172-
fmt.Printf("%25.0fs %s %25d %25d %s %25.2f %25.2f\t", time.Since(start).Seconds(), completionPercentStr, totalCommands, totalErrors, errorPercentStr, messageRate, p50)
180+
fmt.Printf("%25.0fs %s %25d %25d %25d %s %25.2f %25.2f %25.2f\t", time.Since(start).Seconds(), completionPercentStr, totalCommands, totalMessages, totalErrors, errorPercentStr, commandRate, messageRate, p50)
173181
fmt.Printf("\r")
174182
//w.Flush()
175-
if message_limit > 0 && totalCommands >= uint64(message_limit) && !loop {
176-
return true, start, time.Since(start), totalCommands, messageRateTs, percentilesTs, cmdRateTs
183+
if message_limit > 0 && totalMessages >= uint64(message_limit) && !loop {
184+
return true, start, time.Since(start), totalMessages, messageRateTs, percentilesTs, cmdRateTs
177185
}
178186

179187
break
180188
}
181189

182190
case <-c:
183191
fmt.Println("\nreceived Ctrl-c - shutting down")
184-
return true, start, time.Since(start), totalCommands, messageRateTs, percentilesTs, cmdRateTs
192+
return true, start, time.Since(start), totalMessages, messageRateTs, percentilesTs, cmdRateTs
185193
}
186194
}
187195
}

cmd/consumer.go

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/rueian/rueidis"
1212
"github.com/schollz/progressbar/v3"
1313
"github.com/spf13/cobra"
14+
"golang.org/x/time/rate"
1415
"math/rand"
1516
"net/http"
1617
_ "net/http/pprof"
@@ -46,6 +47,8 @@ var consumerCmd = &cobra.Command{
4647
betweenClientsDelay, _ := cmd.Flags().GetDuration("between-clients-duration")
4748
clientKeepAlive, _ := cmd.Flags().GetDuration("client-keep-alive-time")
4849
pprofPort, _ := cmd.Flags().GetInt64("pprof-port")
50+
rps, _ := cmd.Flags().GetInt("rps")
51+
rpsBurst, _ := cmd.Flags().GetInt("rps-burst")
4952

5053
ctx := context.Background()
5154
ips := resolveHostnames(nameserver, host, ctx)
@@ -58,6 +61,23 @@ var consumerCmd = &cobra.Command{
5861
}()
5962
fmt.Printf("Using random seed: %d\n", seed)
6063

64+
var requestRate = Inf
65+
var requestBurst = rps
66+
useRateLimiter := false
67+
if rps != 0 {
68+
requestRate = rate.Limit(rps)
69+
useRateLimiter = true
70+
if rpsBurst != 0 {
71+
requestBurst = rpsBurst
72+
}
73+
} else {
74+
if readBlockMs < 0 {
75+
fmt.Printf("Given you haven't specified a rate of messages/sec and the \"--stream-read-block-ms\" value is also < 0 this means you'll be stressing the server to the highest capacity. Be aware that this will influence the producer side as well!\n")
76+
}
77+
}
78+
79+
var rateLimiter = rate.NewLimiter(requestRate, requestBurst)
80+
6181
client_update_tick := 1
6282
latencies = hdrhistogram.New(1, 90000000, 3)
6383
latenciesTick = hdrhistogram.New(1, 90000000, 3)
@@ -124,7 +144,7 @@ var consumerCmd = &cobra.Command{
124144
for consumerId := 1; uint64(consumerId) <= uint64(consumersForStream); consumerId++ {
125145
consumername := fmt.Sprintf("streamconsumer:%s:%d", groupname, consumerId)
126146
wg.Add(1)
127-
go benchmarkConsumerRoutine(client, c, datapointsChan, &wg, keyname, groupname, consumername, readBlockMs, readCount)
147+
go benchmarkConsumerRoutine(client, c, datapointsChan, &wg, keyname, groupname, consumername, readBlockMs, readCount, useRateLimiter, rateLimiter)
128148
bar.Add(1)
129149
}
130150

@@ -166,9 +186,11 @@ var consumerCmd = &cobra.Command{
166186
},
167187
}
168188

169-
func benchmarkConsumerRoutine(client rueidis.Client, c chan os.Signal, datapointsChan chan datapoint, wg *sync.WaitGroup, keyname, groupname, consumername string, readBlockMs, readCount int64) {
189+
func benchmarkConsumerRoutine(client rueidis.Client, c chan os.Signal, datapointsChan chan datapoint, wg *sync.WaitGroup, keyname, groupname, consumername string, readBlockMs, readCount int64, useRateLimiter bool, rateLimiter *rate.Limiter) {
170190
defer wg.Done()
171191
ctx := context.Background()
192+
var startT time.Time
193+
var endT time.Time
172194

173195
for {
174196
select {
@@ -179,16 +201,26 @@ func benchmarkConsumerRoutine(client rueidis.Client, c chan os.Signal, datapoint
179201
cmdsIssued := make([]int, 0, 1)
180202
cmdsIssued = append(cmdsIssued, XREADGROUP)
181203
processedEntries := 0
182-
startT := time.Now()
183-
xreadEntries, err := client.Do(ctx, client.B().Xreadgroup().Group(groupname, consumername).Count(readCount).Block(readBlockMs).Streams().Key(keyname).Id(">").Build()).AsXRead()
204+
if useRateLimiter {
205+
r := rateLimiter.ReserveN(time.Now(), int(1))
206+
time.Sleep(r.Delay())
207+
}
208+
startT = time.Now()
209+
res := client.Do(ctx, getXreadGroupCmd(client, groupname, consumername, readCount, readBlockMs, keyname))
210+
endT = time.Now()
211+
xreadEntries, err := res.AsXRead()
212+
184213
if err != nil {
185-
cmdsIssued = append(cmdsIssued, XGROUPCREATE)
186-
cmdsIssued = append(cmdsIssued, XGROUPCREATECONSUMER)
187-
cmdsIssued = append(cmdsIssued, XREADGROUP)
188-
err = client.Do(ctx, client.B().XgroupCreate().Key(keyname).Group(groupname).Id("0").Mkstream().Build()).Error()
189-
err = client.Do(ctx, client.B().XgroupCreateconsumer().Key(keyname).Group(groupname).Consumer(consumername).Build()).Error()
190-
startT = time.Now()
191-
xreadEntries, err = client.Do(ctx, client.B().Xreadgroup().Group(groupname, consumername).Count(readCount).Block(readBlockMs).Streams().Key(keyname).Id(">").Build()).AsXRead()
214+
if err != rueidis.Nil {
215+
cmdsIssued = append(cmdsIssued, XGROUPCREATE)
216+
cmdsIssued = append(cmdsIssued, XGROUPCREATECONSUMER)
217+
cmdsIssued = append(cmdsIssued, XREADGROUP)
218+
err = client.Do(ctx, client.B().XgroupCreate().Key(keyname).Group(groupname).Id("0").Mkstream().Build()).Error()
219+
err = client.Do(ctx, client.B().XgroupCreateconsumer().Key(keyname).Group(groupname).Consumer(consumername).Build()).Error()
220+
startT = time.Now()
221+
xreadEntries, err = client.Do(ctx, getXreadGroupCmd(client, groupname, consumername, readCount, readBlockMs, keyname)).AsXRead()
222+
endT = time.Now()
223+
}
192224
}
193225
if err == nil {
194226
xrangeEntries, found := xreadEntries[keyname]
@@ -200,16 +232,24 @@ func benchmarkConsumerRoutine(client rueidis.Client, c chan os.Signal, datapoint
200232
}
201233
}
202234
}
203-
endT := time.Now()
204235
duration := endT.Sub(startT)
205-
datapointsChan <- datapoint{!(err != nil), duration.Microseconds(), cmdsIssued, processedEntries}
236+
datapointsChan <- datapoint{(err == nil || (err != nil && err == rueidis.Nil)), duration.Microseconds(), cmdsIssued, processedEntries}
206237
}
207238
}
208239
}
209240

241+
func getXreadGroupCmd(client rueidis.Client, groupname string, consumername string, readCount int64, readBlockMs int64, keyname string) rueidis.Completed {
242+
cmd := client.B().Xreadgroup().Group(groupname, consumername).Count(readCount)
243+
if readBlockMs >= 0 {
244+
cmd.Block(readBlockMs)
245+
}
246+
xreadGroupCompletedCmd := cmd.Streams().Key(keyname).Id(">")
247+
return xreadGroupCompletedCmd.Build()
248+
}
249+
210250
func init() {
211251
rootCmd.AddCommand(consumerCmd)
212-
consumerCmd.PersistentFlags().Int64("stream-read-block-ms", 30000, "Block the client for this amount of milliseconds.")
252+
consumerCmd.PersistentFlags().Int64("stream-read-block-ms", -1, "Block the client for this amount of milliseconds. If 0 will return immediatelly.")
213253
consumerCmd.PersistentFlags().Uint64("consumers-per-stream-min", 5, "per stream consumer count min.")
214254
consumerCmd.PersistentFlags().Uint64("consumers-per-stream-max", 50, "per stream consumer count max.")
215255
consumerCmd.PersistentFlags().Int64("stream-read-count", 1, "per command count of messages to be read.")

cmd/producer.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
)
2222

2323
var totalCommands uint64
24+
var totalMessages uint64
2425
var totalErrors uint64
2526
var latencies *hdrhistogram.Histogram
2627
var latenciesTick *hdrhistogram.Histogram
@@ -52,8 +53,8 @@ var producerCmd = &cobra.Command{
5253
Run: func(cmd *cobra.Command, args []string) {
5354
host, _ := cmd.Flags().GetString("h")
5455
port, _ := cmd.Flags().GetInt("p")
55-
rps, _ := cmd.Flags().GetInt("rps")
5656
dataLen, _ := cmd.Flags().GetInt("d")
57+
rps, _ := cmd.Flags().GetInt("rps")
5758
rpsBurst, _ := cmd.Flags().GetInt("rps-burst")
5859
numberRequests, _ := cmd.Flags().GetUint64("n")
5960
streamMaxlen, _ := cmd.Flags().GetInt64("stream-maxlen")

cmd/root.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ func init() {
5757
rootCmd.PersistentFlags().String("h", "127.0.0.1", "Server hostname.")
5858
rootCmd.PersistentFlags().Int("p", 6379, "Server port.")
5959
rootCmd.PersistentFlags().Int("rps", 0, "Max rps. If 0 no limit is applied and the DB is stressed up to maximum.")
60-
rootCmd.PersistentFlags().Int("rps-burst", 0, "Max rps burst. If 0 the allowed burst will be the ammount of clients.")
60+
rootCmd.PersistentFlags().Int("rps-burst", 0, "Max rps burst. If 0 the allowed burst will be the amount of clients.")
6161
rootCmd.PersistentFlags().String("a", "", "Password for Redis Auth.")
6262
rootCmd.PersistentFlags().Int64("random-seed", 12345, "random seed to be used.")
6363
rootCmd.PersistentFlags().Uint64("c", 50, "number of clients.")
6464
rootCmd.PersistentFlags().Int64("keyspace-len", 100, "number of streams.")
65-
rootCmd.PersistentFlags().Int64("stream-maxlen", 1000000, "stream max length.")
65+
rootCmd.PersistentFlags().Int64("stream-maxlen", 250000, "stream max length.")
6666
rootCmd.PersistentFlags().Int64("stream-maxlen-expire-secs", 60, "If a stream reached the max length, we expire it after the provided amount of seconds. If 0 will use DEL instead of expire.")
6767
rootCmd.PersistentFlags().String("stream-prefix", "", "stream prefix.")
6868
rootCmd.PersistentFlags().Int("d", 100, "Data size in bytes of the expanded string value sent in the message.")

go.sum

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3
66
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
77
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
88
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
910
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
1011
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
1112
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
1213
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
1314
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
15+
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
1416
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
1517
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
1618
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
@@ -29,15 +31,15 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0
2931
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
3032
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
3133
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
34+
github.com/onsi/gomega v1.27.5 h1:T/X6I0RNFw/kTqgfkZPcQ5KU6vCnWNBGdtrIx2dpGeQ=
3235
github.com/pingcap/errors v0.11.5-0.20211224045212-9687c2b0f87c h1:xpW9bvK+HuuTmyFqUwr+jcCvpVkK7sumiz+ko5H9eq4=
3336
github.com/pingcap/errors v0.11.5-0.20211224045212-9687c2b0f87c/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg=
3437
github.com/pingcap/go-ycsb v1.0.1 h1:OGIUjQjtC22KDHPCqg4+ScWYFZrHQjJnt3Gmf4N8UOw=
3538
github.com/pingcap/go-ycsb v1.0.1/go.mod h1:VQdVCzhVPTDDfWM8NV7c0zZHtDdN//DHtzifn4uYWVc=
39+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3640
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3741
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
3842
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
39-
github.com/rueian/rueidis v0.0.89 h1:Q2TbuNXMJ2d2NegQ47uOoGOGPZLQwRuL0oX/dAlCh6k=
40-
github.com/rueian/rueidis v0.0.89/go.mod h1:LiKWMM/QnILwRfDZIhSIXi4vQqZ/UZy4+/aNkSCt8XA=
4143
github.com/rueian/rueidis v0.0.100 h1:22yp/+8YHuWc/vcrp8bkjeE7baD3vygoh2gZ2+xu1KQ=
4244
github.com/rueian/rueidis v0.0.100/go.mod h1:ivvsRYRtAUcf9OnheuKc5Gpa8IebrkLT1P45Lr2jlXE=
4345
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -51,6 +53,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
5153
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
5254
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
5355
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
56+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
5457
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
5558
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
5659
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -60,6 +63,7 @@ golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL
6063
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
6164
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
6265
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
66+
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw=
6367
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
6468
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
6569
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
@@ -70,6 +74,7 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
7074
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
7175
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
7276
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
77+
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
7378
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
7479
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
7580
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -80,6 +85,7 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
8085
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
8186
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
8287
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
88+
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
8389
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
8490
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
8591
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -91,12 +97,14 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
9197
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
9298
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
9399
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
100+
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
94101
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
95102
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
96103
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
97104
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
98105
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
99106
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
100107
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
108+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
101109
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
102110
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

0 commit comments

Comments
 (0)