Skip to content

Commit 4789502

Browse files
Part of tarantool#645 bench: load profile patch (select and update)
user@cartridge-cli % ./cartridge bench --select=30 --update=30 --insert=40 --fill=100000 Tarantool 2.8.2 (Binary) f4897ffe-98dd-40fc-a6f2-21ca8bb52fe7 Parameters: URL: 127.0.0.1:3301 user: guest connections: 10 simultaneous requests: 10 duration: 10 seconds key size: 10 bytes data size: 20 bytes insert: 40 percentages select: 30 percentages update: 30 percentages Data schema | key | value | ------------------------------ | ------------------------------ | random(10) | random(20) The pre-filling of the space has started, because the insert operation is not specified or there was an explicit instruction for pre-filling ... Pre-filling is finished Benchmark start ... Benchmark stop Results: Success operations: 1430826 Failed operations: 0 Request count: 1432454 Time (seconds): 10.000640 Requests per second: 143236
1 parent f04035c commit 4789502

File tree

7 files changed

+251
-18
lines changed

7 files changed

+251
-18
lines changed

cli/bench/bench.go

+81-16
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"time"
99

1010
"github.com/FZambia/tarantool"
11-
"github.com/tarantool/cartridge-cli/cli/common"
1211
"github.com/tarantool/cartridge-cli/cli/context"
1312
)
1413

@@ -22,14 +21,25 @@ func printResults(results Results) {
2221
fmt.Printf("\tRequests per second: %d\n\n", results.requestsPerSecond)
2322
}
2423

24+
// verifyOperationsPercentage checks that the amount of operations percentage is 100.
25+
func verifyOperationsPercentage(ctx *context.BenchCtx) error {
26+
entire_percentage := ctx.InsertCount + ctx.SelectCount + ctx.UpdateCount
27+
if entire_percentage != 100 {
28+
return fmt.Errorf(
29+
"The number of operations as a percentage should be equal to 100, " +
30+
"note that by default the percentage of inserts is 100")
31+
}
32+
return nil
33+
}
34+
2535
// spacePreset prepares space for a benchmark.
2636
func spacePreset(ctx context.BenchCtx, tarantoolConnection *tarantool.Connection) error {
2737
dropBenchmarkSpace(tarantoolConnection)
2838
return createBenchmarkSpace(tarantoolConnection)
2939
}
3040

3141
// incrementRequest increases the counter of successful/failed requests depending on the presence of an error.
32-
func incrementRequest(err error, results *Results) {
42+
func (results *Results) incrementRequestsCounters(err error) {
3343
if err == nil {
3444
results.successResultCount++
3545
} else {
@@ -39,29 +49,30 @@ func incrementRequest(err error, results *Results) {
3949
}
4050

4151
// requestsLoop continuously executes the insert query until the benchmark time runs out.
42-
func requestsLoop(ctx context.BenchCtx, tarantoolConnection *tarantool.Connection, results *Results, backgroundCtx bctx.Context) {
52+
func requestsLoop(requestsSequence *RequestsSequence, backgroundCtx bctx.Context) {
4353
for {
4454
select {
4555
case <-backgroundCtx.Done():
4656
return
4757
default:
48-
_, err := tarantoolConnection.Exec(
49-
tarantool.Insert(
50-
benchSpaceName,
51-
[]interface{}{common.RandomString(ctx.KeySize), common.RandomString(ctx.DataSize)}))
52-
incrementRequest(err, results)
58+
requestsSequence.getNext().execute()
5359
}
5460
}
5561
}
5662

57-
// connectionLoop runs "ctx.SimultaneousRequests" requests execution threads through the same connection.
58-
func connectionLoop(ctx context.BenchCtx, tarantoolConnection *tarantool.Connection, results *Results, backgroundCtx bctx.Context) {
63+
// connectionLoop runs "ctx.SimultaneousRequests" requests execution threads
64+
// through the same connection.
65+
func connectionLoop(
66+
ctx *context.BenchCtx,
67+
requestsSequence RequestsSequence,
68+
backgroundCtx bctx.Context,
69+
) {
5970
var connectionWait sync.WaitGroup
6071
for i := 0; i < ctx.SimultaneousRequests; i++ {
6172
connectionWait.Add(1)
6273
go func() {
6374
defer connectionWait.Done()
64-
requestsLoop(ctx, tarantoolConnection, results, backgroundCtx)
75+
requestsLoop(&requestsSequence, backgroundCtx)
6576
}()
6677
}
6778

@@ -72,6 +83,10 @@ func connectionLoop(ctx context.BenchCtx, tarantoolConnection *tarantool.Connect
7283
func Run(ctx context.BenchCtx) error {
7384
rand.Seed(time.Now().UnixNano())
7485

86+
if err := verifyOperationsPercentage(&ctx); err != nil {
87+
return err
88+
}
89+
7590
// Connect to tarantool and preset space for benchmark.
7691
tarantoolConnection, err := tarantool.Connect(ctx.URL, tarantool.Opts{
7792
User: ctx.User,
@@ -90,7 +105,8 @@ func Run(ctx context.BenchCtx) error {
90105
return err
91106
}
92107

93-
/// Сreate a "connectionPool" before starting the benchmark to exclude the connection establishment time from measurements.
108+
// Сreate a "connectionPool" before starting the benchmark
109+
// to exclude the connection establishment time from measurements.
94110
connectionPool := make([]*tarantool.Connection, ctx.Connections)
95111
for i := 0; i < ctx.Connections; i++ {
96112
connectionPool[i], err = tarantool.Connect(ctx.URL, tarantool.Opts{
@@ -103,13 +119,28 @@ func Run(ctx context.BenchCtx) error {
103119
defer connectionPool[i].Close()
104120
}
105121

106-
fmt.Println("Benchmark start")
122+
// Pre-fill benchmark space if required.
123+
if ctx.InsertCount == 0 || ctx.PreFillingCount != PreFillingCount {
124+
fmt.Println("The pre-filling of the space has started,\n" +
125+
"because the insert operation is not specified\n" +
126+
"or there was an explicit instruction for pre-filling.")
127+
fmt.Println("...")
128+
fillBenchmarkSpace(ctx, connectionPool)
129+
fmt.Printf("Pre-filling is finished.\n\n")
130+
}
131+
132+
fmt.Println("Benchmark start.")
107133
fmt.Println("...")
108134

109135
// The "context" will be used to stop all "connectionLoop" when the time is out.
110136
backgroundCtx, cancel := bctx.WithCancel(bctx.Background())
111137
var waitGroup sync.WaitGroup
112138
results := Results{}
139+
getRandomTupleCommand := fmt.Sprintf(
140+
"box.space.%s.index.%s:random",
141+
benchSpaceName,
142+
benchSpacePrimaryIndexName,
143+
)
113144

114145
startTime := time.Now()
115146
timer := time.NewTimer(time.Duration(ctx.Duration * int(time.Second)))
@@ -119,7 +150,39 @@ func Run(ctx context.BenchCtx) error {
119150
waitGroup.Add(1)
120151
go func(connection *tarantool.Connection) {
121152
defer waitGroup.Done()
122-
connectionLoop(ctx, connection, &results, backgroundCtx)
153+
requestsSequence := RequestsSequence{
154+
[]RequestsGenerator{
155+
{
156+
InsertRequest{
157+
ctx,
158+
connection,
159+
&results,
160+
},
161+
ctx.InsertCount,
162+
},
163+
{
164+
SelectRequest{
165+
ctx,
166+
connection,
167+
&results,
168+
getRandomTupleCommand,
169+
},
170+
ctx.SelectCount,
171+
},
172+
{
173+
UpdateRequest{
174+
ctx,
175+
connection,
176+
&results,
177+
getRandomTupleCommand,
178+
},
179+
ctx.UpdateCount,
180+
},
181+
},
182+
0,
183+
ctx.InsertCount,
184+
}
185+
connectionLoop(&ctx, requestsSequence, backgroundCtx)
123186
}(connectionPool[i])
124187
}
125188
// Sends "signal" to all "connectionLoop" and waits for them to complete.
@@ -130,8 +193,10 @@ func Run(ctx context.BenchCtx) error {
130193
results.duration = time.Since(startTime).Seconds()
131194
results.requestsPerSecond = int(float64(results.handledRequestsCount) / results.duration)
132195

133-
dropBenchmarkSpace(tarantoolConnection)
134-
fmt.Println("Benchmark stop")
196+
if err := dropBenchmarkSpace(tarantoolConnection); err != nil {
197+
return err
198+
}
199+
fmt.Println("Benchmark stop.")
135200

136201
printResults(results)
137202
return nil

cli/bench/config.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ import (
99
"github.com/tarantool/cartridge-cli/cli/context"
1010
)
1111

12-
var (
12+
const (
1313
benchSpaceName = "__benchmark_space__"
1414
benchSpacePrimaryIndexName = "__bench_primary_key__"
15+
PreFillingCount = 1000000
1516
)
1617

1718
// printConfig output formatted config parameters.
@@ -25,10 +26,15 @@ func printConfig(ctx context.BenchCtx, tarantoolConnection *tarantool.Connection
2526
fmt.Printf("\tduration: %d seconds\n", ctx.Duration)
2627
fmt.Printf("\tkey size: %d bytes\n", ctx.KeySize)
2728
fmt.Printf("\tdata size: %d bytes\n", ctx.DataSize)
29+
fmt.Printf("\tinsert: %d percentages\n", ctx.InsertCount)
30+
fmt.Printf("\tselect: %d percentages\n", ctx.SelectCount)
31+
fmt.Printf("\tupdate: %d percentages\n\n", ctx.UpdateCount)
32+
2833
fmt.Printf("Data schema\n")
2934
w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
3035
fmt.Fprintf(w, "|\tkey\t|\tvalue\n")
3136
fmt.Fprintf(w, "|\t------------------------------\t|\t------------------------------\n")
3237
fmt.Fprintf(w, "|\trandom(%d)\t|\trandom(%d)\n", ctx.KeySize, ctx.DataSize)
3338
w.Flush()
39+
fmt.Println()
3440
}

cli/bench/requests.go

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package bench
2+
3+
import (
4+
"math/rand"
5+
"reflect"
6+
7+
"github.com/FZambia/tarantool"
8+
"github.com/tarantool/cartridge-cli/cli/common"
9+
)
10+
11+
// Execute insert operation.
12+
func (request InsertRequest) execute() {
13+
_, err := request.tarantoolConnection.Exec(
14+
tarantool.Insert(
15+
benchSpaceName,
16+
[]interface{}{
17+
common.RandomString(request.ctx.KeySize),
18+
common.RandomString(request.ctx.DataSize),
19+
}))
20+
request.results.incrementRequestsCounters(err)
21+
}
22+
23+
// Execute select operation.
24+
func (request SelectRequest) execute() {
25+
_, err := request.tarantoolConnection.Exec(tarantool.Call(
26+
request.getRandomTupleCommand,
27+
[]interface{}{rand.Int()}))
28+
request.results.incrementRequestsCounters(err)
29+
}
30+
31+
// Execute update operation.
32+
func (request UpdateRequest) execute() {
33+
getRandomTupleResponse, err := request.tarantoolConnection.Exec(
34+
tarantool.Call(request.getRandomTupleCommand,
35+
[]interface{}{rand.Int()}))
36+
if err == nil {
37+
data := getRandomTupleResponse.Data
38+
if len(data) > 0 {
39+
key := reflect.ValueOf(data[0]).Index(0).Elem().String()
40+
_, err := request.tarantoolConnection.Exec(
41+
tarantool.Update(
42+
benchSpaceName,
43+
benchSpacePrimaryIndexName,
44+
[]interface{}{key},
45+
[]tarantool.Op{tarantool.Op(
46+
tarantool.OpAssign(
47+
2,
48+
common.RandomString(request.ctx.DataSize)))}))
49+
request.results.incrementRequestsCounters(err)
50+
}
51+
}
52+
}
53+
54+
// getNext return next operation in operations sequence.
55+
func (requestsSequence *RequestsSequence) getNext() Request {
56+
// If at the moment the number of remaining requests = 0,
57+
// then find a new generator, which requests count > 0.
58+
// If new generator has requests count = 0, then repeat.
59+
for requestsSequence.currentCounter == 0 {
60+
// Increase the index, which means logical switching to a new generator.
61+
requestsSequence.currentRequestIndex++
62+
requestsSequence.currentRequestIndex %= len(requestsSequence.requests)
63+
// Get new generator by index.
64+
nextRequestsGenerator := requestsSequence.requests[requestsSequence.currentRequestIndex]
65+
// Get requests count for new operation.
66+
requestsSequence.currentCounter = nextRequestsGenerator.count
67+
}
68+
// Logical taking of a single request.
69+
requestsSequence.currentCounter--
70+
return requestsSequence.requests[requestsSequence.currentRequestIndex].request
71+
}

cli/bench/space.go

+33-1
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@ package bench
33
import (
44
"fmt"
55
"reflect"
6+
"sync"
67

78
"github.com/FZambia/tarantool"
9+
"github.com/tarantool/cartridge-cli/cli/common"
10+
"github.com/tarantool/cartridge-cli/cli/context"
811
)
912

1013
// createBenchmarkSpace creates benchmark space with formatting and primary index.
1114
func createBenchmarkSpace(tarantoolConnection *tarantool.Connection) error {
1215
// Creating space.
1316
createCommand := "return box.schema.space.create(...).name"
14-
_, err := tarantoolConnection.Exec(tarantool.Eval(createCommand, []interface{}{benchSpaceName, map[string]bool{"if_not_exists": true}}))
17+
_, err := tarantoolConnection.Exec(tarantool.Eval(
18+
createCommand,
19+
[]interface{}{benchSpaceName, map[string]bool{"if_not_exists": true}},
20+
))
1521
if err != nil {
1622
return err
1723
}
@@ -54,3 +60,29 @@ func dropBenchmarkSpace(tarantoolConnection *tarantool.Connection) error {
5460
}
5561
return nil
5662
}
63+
64+
// fillBenchmarkSpace fills benchmark space
65+
// with a PreFillingCount number of records
66+
// using connectionPool for fast filling.
67+
func fillBenchmarkSpace(ctx context.BenchCtx, connectionPool []*tarantool.Connection) {
68+
var waitGroup sync.WaitGroup
69+
filledCount := 0
70+
for i := 0; i < ctx.Connections; i++ {
71+
waitGroup.Add(1)
72+
go func(tarantoolConnection *tarantool.Connection) {
73+
defer waitGroup.Done()
74+
for filledCount < ctx.PreFillingCount {
75+
tarantoolConnection.Exec(tarantool.Insert(
76+
benchSpaceName,
77+
[]interface{}{
78+
common.RandomString(ctx.KeySize),
79+
common.RandomString(ctx.DataSize),
80+
},
81+
))
82+
filledCount++
83+
}
84+
return
85+
}(connectionPool[i])
86+
}
87+
waitGroup.Wait()
88+
}

cli/bench/types.go

+49
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package bench
22

3+
import (
4+
"github.com/FZambia/tarantool"
5+
"github.com/tarantool/cartridge-cli/cli/context"
6+
)
7+
38
// Results describes set of benchmark results.
49
type Results struct {
510
handledRequestsCount int // Count of all executed requests.
@@ -8,3 +13,47 @@ type Results struct {
813
duration float64 // Benchmark duration.
914
requestsPerSecond int // Cumber of requests per second - the main measured value.
1015
}
16+
17+
// Request is nterface for various types of requests.
18+
type Request interface {
19+
execute()
20+
}
21+
22+
// InsertRequest data structure for insert request.
23+
type InsertRequest struct {
24+
ctx context.BenchCtx
25+
tarantoolConnection *tarantool.Connection
26+
results *Results
27+
}
28+
29+
// SelectRequest data structure for select request.
30+
type SelectRequest struct {
31+
ctx context.BenchCtx
32+
tarantoolConnection *tarantool.Connection
33+
results *Results
34+
getRandomTupleCommand string
35+
}
36+
37+
// UpdateRequest data structure for update request.
38+
type UpdateRequest struct {
39+
ctx context.BenchCtx
40+
tarantoolConnection *tarantool.Connection
41+
results *Results
42+
getRandomTupleCommand string
43+
}
44+
45+
// RequestsGenerator data structure for abstraction of a renewable heap of identical requests.
46+
type RequestsGenerator struct {
47+
request Request // InsertRequest, SelectRequest or UpdateRequest.
48+
count int // Count of requests.
49+
}
50+
51+
// RequestsSequence data structure for abstraction for the constant issuance of new requests.
52+
type RequestsSequence struct {
53+
requests []RequestsGenerator
54+
// currentRequestIndex describes what type of request will be issued by the sequence.
55+
currentRequestIndex int
56+
// currentCounter describes how many requests of the same type
57+
// are left to issue from RequestsPool.
58+
currentCounter int
59+
}

0 commit comments

Comments
 (0)