Skip to content

Commit def9e95

Browse files
bench: add the ability to set a load profile
Patch for benchmark tool with load profile and space pre-filling. Added: - Flag "insert" - percentage of insert operations to bench space. - Flag "select" - percentage of select operations from bench space. - Flag "update" - percentage of update operations in bench space. - Flag "fill" - number of records to pre-fill the space. ``` user@cartridge-cli % ./cartridge bench --select=30 --update=30 --insert=40 --fill=100000 Tarantool 2.8.3 (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. Number of records: 100000 Benchmark start. ... Benchmark stop. Results: Success operations: 1332979 Failed operations: 0 Request count: 1334004 Time (seconds): 10.000733 Requests per second: 133390 ``` Part of tarantool#645
1 parent 59fd4fe commit def9e95

File tree

9 files changed

+310
-12
lines changed

9 files changed

+310
-12
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
- Updated `cartridge` to `2.7.4` and `metrics` to `0.13.0` in application template.
1313

14+
### Added
15+
16+
- Tarantool benchmark tool update (select and update operations):
17+
* option --insert has been added - sets percentage of insert operations to bench space.
18+
* option --select has been added - sets percentage of select operations from bench space.
19+
* option --update has been added - sets percentage of update operations in bench space.
20+
* option --fill" has been added - sets number of records to pre-fill the space.
21+
1422
## [2.12.1] - 2022-05-26
1523

1624
### Changed

cli/bench/bench.go

+83-12
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,39 +49,62 @@ 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+
request := requestsSequence.getNext()
59+
request.operation(&request)
5360
}
5461
}
5562
}
5663

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) {
64+
// connectionLoop runs "ctx.SimultaneousRequests" requests execution goroutines
65+
// through the same connection.
66+
func connectionLoop(
67+
ctx *context.BenchCtx,
68+
requestsSequence *RequestsSequence,
69+
backgroundCtx bctx.Context,
70+
) {
5971
var connectionWait sync.WaitGroup
6072
for i := 0; i < ctx.SimultaneousRequests; i++ {
6173
connectionWait.Add(1)
6274
go func() {
6375
defer connectionWait.Done()
64-
requestsLoop(ctx, tarantoolConnection, results, backgroundCtx)
76+
requestsLoop(requestsSequence, backgroundCtx)
6577
}()
6678
}
6779

6880
connectionWait.Wait()
6981
}
7082

83+
// preFillBenchmarkSpaceIfRequired fills benchmark space
84+
// if insert count = 0 or PreFillingCount flag is explicitly specified.
85+
func preFillBenchmarkSpaceIfRequired(ctx context.BenchCtx, connectionPool []*tarantool.Connection) error {
86+
if ctx.InsertCount == 0 || ctx.PreFillingCount != PreFillingCount {
87+
fmt.Println("\nThe pre-filling of the space has started,\n" +
88+
"because the insert operation is not specified\n" +
89+
"or there was an explicit instruction for pre-filling.")
90+
fmt.Println("...")
91+
filledCount, err := fillBenchmarkSpace(ctx, connectionPool)
92+
if err != nil {
93+
return err
94+
}
95+
fmt.Printf("Pre-filling is finished. Number of records: %d\n\n", filledCount)
96+
}
97+
return nil
98+
}
99+
71100
// Main benchmark function.
72101
func Run(ctx context.BenchCtx) error {
73102
rand.Seed(time.Now().UnixNano())
74103

104+
if err := verifyOperationsPercentage(&ctx); err != nil {
105+
return err
106+
}
107+
75108
// Connect to tarantool and preset space for benchmark.
76109
tarantoolConnection, err := tarantool.Connect(ctx.URL, tarantool.Opts{
77110
User: ctx.User,
@@ -103,6 +136,10 @@ func Run(ctx context.BenchCtx) error {
103136
defer connectionPool[i].Close()
104137
}
105138

139+
if err := preFillBenchmarkSpaceIfRequired(ctx, connectionPool); err != nil {
140+
return err
141+
}
142+
106143
fmt.Println("Benchmark start")
107144
fmt.Println("...")
108145

@@ -119,7 +156,41 @@ func Run(ctx context.BenchCtx) error {
119156
waitGroup.Add(1)
120157
go func(connection *tarantool.Connection) {
121158
defer waitGroup.Done()
122-
connectionLoop(ctx, connection, &results, backgroundCtx)
159+
requestsSequence := RequestsSequence{
160+
[]RequestsGenerator{
161+
{
162+
Request{
163+
insertOperation,
164+
ctx,
165+
connection,
166+
&results,
167+
},
168+
ctx.InsertCount,
169+
},
170+
{
171+
Request{
172+
selectOperation,
173+
ctx,
174+
connection,
175+
&results,
176+
},
177+
ctx.SelectCount,
178+
},
179+
{
180+
Request{
181+
updateOperation,
182+
ctx,
183+
connection,
184+
&results,
185+
},
186+
ctx.UpdateCount,
187+
},
188+
},
189+
0,
190+
ctx.InsertCount,
191+
sync.Mutex{},
192+
}
193+
connectionLoop(&ctx, &requestsSequence, backgroundCtx)
123194
}(connectionPool[i])
124195
}
125196
// Sends "signal" to all "connectionLoop" and waits for them to complete.

cli/bench/config.go

+10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ import (
1212
var (
1313
benchSpaceName = "__benchmark_space__"
1414
benchSpacePrimaryIndexName = "__bench_primary_key__"
15+
PreFillingCount = 1000000
16+
getRandomTupleCommand = fmt.Sprintf(
17+
"box.space.%s.index.%s:random",
18+
benchSpaceName,
19+
benchSpacePrimaryIndexName,
20+
)
1521
)
1622

1723
// printConfig output formatted config parameters.
@@ -25,6 +31,10 @@ func printConfig(ctx context.BenchCtx, tarantoolConnection *tarantool.Connection
2531
fmt.Printf("\tduration: %d seconds\n", ctx.Duration)
2632
fmt.Printf("\tkey size: %d bytes\n", ctx.KeySize)
2733
fmt.Printf("\tdata size: %d bytes\n", ctx.DataSize)
34+
fmt.Printf("\tinsert: %d percentages\n", ctx.InsertCount)
35+
fmt.Printf("\tselect: %d percentages\n", ctx.SelectCount)
36+
fmt.Printf("\tupdate: %d percentages\n\n", ctx.UpdateCount)
37+
2838
fmt.Printf("Data schema\n")
2939
w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
3040
fmt.Fprintf(w, "|\tkey\t|\tvalue\n")

cli/bench/requests.go

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
// insertOperation execute insert operation.
12+
func insertOperation(request *Request) {
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+
// selectOperation execute select operation.
24+
func selectOperation(request *Request) {
25+
_, err := request.tarantoolConnection.Exec(tarantool.Call(
26+
getRandomTupleCommand,
27+
[]interface{}{rand.Int()}))
28+
request.results.incrementRequestsCounters(err)
29+
}
30+
31+
// updateOperation execute update operation.
32+
func updateOperation(request *Request) {
33+
getRandomTupleResponse, err := request.tarantoolConnection.Exec(
34+
tarantool.Call(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+
requestsSequence.findNewRequestsGeneratorMutex.Lock()
60+
defer requestsSequence.findNewRequestsGeneratorMutex.Unlock()
61+
for requestsSequence.currentCounter == 0 {
62+
// Increase the index, which means logical switching to a new generator.
63+
requestsSequence.currentRequestIndex++
64+
requestsSequence.currentRequestIndex %= len(requestsSequence.requests)
65+
// Get new generator by index.
66+
nextRequestsGenerator := requestsSequence.requests[requestsSequence.currentRequestIndex]
67+
// Get requests count for new operation.
68+
requestsSequence.currentCounter = nextRequestsGenerator.count
69+
}
70+
// Logical taking of a single request.
71+
requestsSequence.currentCounter--
72+
return requestsSequence.requests[requestsSequence.currentRequestIndex].request
73+
}

cli/bench/space.go

+82
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package bench
22

33
import (
4+
bctx "context"
45
"fmt"
56
"reflect"
7+
"sync"
68

79
"github.com/FZambia/tarantool"
10+
"github.com/tarantool/cartridge-cli/cli/common"
11+
"github.com/tarantool/cartridge-cli/cli/context"
812
)
913

1014
// createBenchmarkSpace creates benchmark space with formatting and primary index.
@@ -54,3 +58,81 @@ func dropBenchmarkSpace(tarantoolConnection *tarantool.Connection) error {
5458
}
5559
return nil
5660
}
61+
62+
// fillBenchmarkSpace fills benchmark space with a PreFillingCount number of records
63+
// using connectionPool for fast filling.
64+
func fillBenchmarkSpace(ctx context.BenchCtx, connectionPool []*tarantool.Connection) (int, error) {
65+
var insertMutex sync.Mutex
66+
var waitGroup sync.WaitGroup
67+
filledCount := 0
68+
errorChan := make(chan error, ctx.Connections)
69+
backgroundCtx, cancel := bctx.WithCancel(bctx.Background())
70+
71+
for i := 0; i < ctx.Connections; i++ {
72+
waitGroup.Add(1)
73+
go func(tarantoolConnection *tarantool.Connection) {
74+
defer waitGroup.Done()
75+
for filledCount < ctx.PreFillingCount && len(errorChan) == 0 {
76+
select {
77+
case <-backgroundCtx.Done():
78+
return
79+
default:
80+
// Lock mutex for checking extra iteration and increment counter.
81+
insertMutex.Lock()
82+
if filledCount == ctx.PreFillingCount {
83+
insertMutex.Unlock()
84+
return
85+
}
86+
filledCount++
87+
insertMutex.Unlock()
88+
_, err := tarantoolConnection.Exec(tarantool.Insert(
89+
benchSpaceName,
90+
[]interface{}{
91+
common.RandomString(ctx.KeySize),
92+
common.RandomString(ctx.DataSize),
93+
},
94+
))
95+
if err != nil {
96+
fmt.Println(err)
97+
errorChan <- err
98+
return
99+
}
100+
}
101+
}
102+
}(connectionPool[i])
103+
}
104+
105+
// Goroutine for checking error in channel.
106+
go func() {
107+
for {
108+
select {
109+
case <-backgroundCtx.Done():
110+
return
111+
default:
112+
if len(errorChan) > 0 {
113+
// Stop "insert" goroutines.
114+
cancel()
115+
return
116+
}
117+
}
118+
}
119+
}()
120+
121+
waitGroup.Wait()
122+
// Stop all goroutines.
123+
// If "error" goroutine stopped others "insert" goroutines, "error" goroutine stops itself.
124+
// If "insert" goroutine successfully completed, then need to stop "error" goroutine.
125+
cancel()
126+
127+
// Check if we have an error.
128+
if len(errorChan) > 0 {
129+
err := <-errorChan
130+
close(errorChan)
131+
return filledCount, fmt.Errorf(
132+
"Error during space pre-filling: %s.",
133+
err.Error())
134+
}
135+
close(errorChan)
136+
137+
return filledCount, nil
138+
}

0 commit comments

Comments
 (0)