Skip to content

Commit c2162fb

Browse files
author
Tim Middleton
committed
Updates to perf tests
1 parent a740e26 commit c2162fb

File tree

3 files changed

+258
-18
lines changed

3 files changed

+258
-18
lines changed

.github/workflows/build-compatability-v1-1412.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
fail-fast: false
2424
matrix:
2525
coherenceVersion:
26-
- 14.1.2-0-1
26+
- 14.1.2-0-2
2727
- 14.1.2-0-2-SNAPSHOT
2828
go-version:
2929
- 1.23.x

test/e2e/perf/run_test.go

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,151 @@ package perf
88

99
import (
1010
"github.com/onsi/gomega"
11+
"github.com/oracle/coherence-go-client/v2/coherence"
12+
"github.com/oracle/coherence-go-client/v2/coherence/aggregators"
13+
"github.com/oracle/coherence-go-client/v2/coherence/filters"
1114
"testing"
1215
)
1316

14-
// TestPerformance1 tests for discovery
15-
func TestPerformance1(t *testing.T) {
17+
var (
18+
filterIDBetween = filters.Between(idExtractor, 125_000, 150_000)
19+
filterCountryAU = filters.Equal(countryExtractor, "Australia")
20+
filterMultiple = filters.Equal(countryExtractor, "Australia").And(filterIDBetween)
21+
countryInFilter = filters.In(countryExtractor, []string{"Mexico", "Australia"})
22+
)
23+
24+
// TestStreamingPerformance tests streaming.
25+
func TestStreamingPerformance(t *testing.T) {
26+
var iterations int64 = 10
27+
28+
testCases := []struct {
29+
testName string
30+
test func(t *testing.T, filter filters.Filter, iterations int64) *PerformanceResult
31+
filter filters.Filter
32+
count int64
33+
}{
34+
{"FilterBetween", RunTestFilterEntrySet, filterIDBetween, iterations},
35+
{"FilterEquals", RunTestFilterEntrySet, filterCountryAU, iterations},
36+
{"FilterMultiple", RunTestFilterEntrySet, filterMultiple, iterations},
37+
{"FilterAlways", RunTestFilterEntrySet, filters.Always(), iterations},
38+
{"FilterIn", RunTestFilterEntrySet, countryInFilter, iterations},
39+
}
40+
for _, tc := range testCases {
41+
t.Run(tc.testName, func(t *testing.T) {
42+
result := tc.test(t, tc.filter, tc.count)
43+
mapResults[tc.testName] = result
44+
})
45+
}
46+
}
47+
48+
// TestStreamingPerformance tests streaming.
49+
func TestAggregatorPerformance(t *testing.T) {
50+
var iterations int64 = 10
51+
52+
testCases := []struct {
53+
testName string
54+
test func(t *testing.T, filter filters.Filter, iterations int64) *PerformanceResult
55+
filter filters.Filter
56+
count int64
57+
}{
58+
{"AggregatorCountAllFilter", RunCountAggregationTest, nil, iterations},
59+
{"AggregatorCountCountryFilter", RunCountAggregationTest, filterCountryAU, iterations},
60+
}
61+
for _, tc := range testCases {
62+
t.Run(tc.testName, func(t *testing.T) {
63+
result := tc.test(t, tc.filter, tc.count)
64+
mapResults[tc.testName] = result
65+
})
66+
}
67+
}
68+
69+
// TestKeyOperators tests key operations.
70+
func TestKeyOperators(t *testing.T) {
71+
var iterations int64 = 100_000
72+
73+
testCases := []struct {
74+
testName string
75+
test func(t *testing.T, operation string, iterations int64) *PerformanceResult
76+
operation string
77+
count int64
78+
}{
79+
{"KeyGet", RunTestKeyOperation, "get", iterations},
80+
{"KeyPut", RunTestKeyOperation, "put", iterations},
81+
{"KeyContainsKey", RunTestKeyOperation, "containsKey", iterations},
82+
}
83+
for _, tc := range testCases {
84+
t.Run(tc.testName, func(t *testing.T) {
85+
result := tc.test(t, tc.operation, tc.count)
86+
mapResults[tc.testName] = result
87+
})
88+
}
89+
}
90+
91+
// RunTestFilterEntrySet runs tests against various filters.
92+
func RunTestFilterEntrySet(t *testing.T, filter filters.Filter, count int64) *PerformanceResult {
1693
var (
1794
g = gomega.NewWithT(t)
95+
i int64
1896
)
1997

98+
timer := newTestTimer(count)
99+
for i = 0; i < count; i++ {
100+
timer.Start()
101+
for ch := range config.Students.EntrySetFilter(ctx, filter) {
102+
g.Expect(ch.Err).To(gomega.BeNil())
103+
_ = ch.Value
104+
}
105+
timer.End()
106+
}
107+
108+
return timer.Complete()
109+
}
110+
111+
// RunCountAggregationTest runs tests against various aggregators.
112+
func RunCountAggregationTest(t *testing.T, filter filters.Filter, count int64) *PerformanceResult {
113+
var (
114+
g = gomega.NewWithT(t)
115+
i int64
116+
fltr = filters.Always()
117+
)
118+
119+
if filter != nil {
120+
fltr = filter
121+
}
122+
123+
timer := newTestTimer(count)
124+
for i = 0; i < count; i++ {
125+
timer.Start()
126+
_, err := coherence.AggregateFilter[int, Student](ctx, config.Students, fltr, aggregators.Count())
127+
g.Expect(err).To(gomega.BeNil())
128+
timer.End()
129+
}
130+
131+
return timer.Complete()
132+
}
133+
134+
// RunTestKeyOperation runs key based tests.
135+
func RunTestKeyOperation(t *testing.T, operation string, count int64) *PerformanceResult {
136+
g := gomega.NewWithT(t)
137+
20138
size, err := config.Students.Size(ctx)
21-
g.Expect(err).To(gomega.BeNil())
22-
g.Expect(size).To(gomega.Equal(maxStudents))
139+
g.Expect(err).NotTo(gomega.HaveOccurred())
140+
141+
timer := newTestTimer(count)
142+
for i := 0; i < int(count); i++ {
143+
id := rnd.Intn(size) + 1
144+
timer.Start()
145+
if operation == "get" {
146+
_, err = config.Students.Get(ctx, id)
147+
} else if operation == "put" {
148+
_, err = config.Students.Put(ctx, id, getRandomStudent(id))
149+
} else if operation == "containsKey" {
150+
_, err = config.Students.ContainsKey(ctx, id)
151+
}
152+
153+
g.Expect(err).To(gomega.BeNil())
154+
timer.End()
155+
}
156+
157+
return timer.Complete()
23158
}

test/e2e/perf/suite_test.go

Lines changed: 118 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ import (
1010
"context"
1111
"fmt"
1212
"github.com/oracle/coherence-go-client/v2/coherence"
13+
"github.com/oracle/coherence-go-client/v2/coherence/extractors"
1314
"log"
1415
"math/rand"
1516
"os"
17+
"sort"
1618
"testing"
1719
"time"
1820
)
@@ -30,11 +32,23 @@ type Config struct {
3032
Students coherence.NamedCache[int, Student]
3133
}
3234

35+
type PerformanceResult struct {
36+
Executions int64
37+
TotalTime int64
38+
MinTime time.Duration
39+
MaxTime time.Duration
40+
}
41+
3342
var (
3443
ctx = context.Background()
35-
//nolint:gosec // just a test
36-
rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
44+
//nolint:gosec // just a test - have something consistent
45+
rnd = rand.New(rand.NewSource(123_456_789))
3746
config = Config{}
47+
48+
courseExtractor = extractors.Extract[string]("course")
49+
countryExtractor = extractors.Extract[string]("country")
50+
idExtractor = extractors.Extract[int]("id")
51+
mapResults = make(map[string]*PerformanceResult)
3852
)
3953

4054
const (
@@ -59,24 +73,66 @@ func TestMain(m *testing.M) {
5973
errorAndExit("unable to clear", err)
6074
}
6175

76+
// Add Indexes
77+
err = coherence.AddIndex[int, Student](ctx, config.Students, courseExtractor, true)
78+
if err != nil {
79+
errorAndExit("unable to add index", err)
80+
}
81+
82+
err = coherence.AddIndex[int, Student](ctx, config.Students, countryExtractor, true)
83+
if err != nil {
84+
errorAndExit("unable to add index", err)
85+
}
86+
87+
err = coherence.AddIndex[int, Student](ctx, config.Students, idExtractor, true)
88+
if err != nil {
89+
errorAndExit("unable to add index", err)
90+
}
91+
6292
log.Println("Populating cache with", maxStudents, "students")
6393
if populateCache(config.Students, maxStudents) != nil {
6494
errorAndExit("failed to populate cache", err)
6595
}
6696

6797
size, err = config.Students.Size(ctx)
68-
if err != nil || size != maxStudents {
69-
errorAndExit("size not correct", err)
98+
if err != nil {
99+
errorAndExit("error", err)
100+
}
101+
if size != maxStudents {
102+
errorAndExit("count", fmt.Errorf("invalid number of students: %d", size))
70103
}
71104
log.Println("Cache size is", size)
72105

73106
exitCode := m.Run()
74107

75108
fmt.Printf("Tests completed with return code %d\n", exitCode)
109+
printResults()
76110

77111
os.Exit(exitCode)
78112
}
79113

114+
func printResults() {
115+
keys := make([]string, 0, len(mapResults))
116+
for k := range mapResults {
117+
keys = append(keys, k)
118+
}
119+
sort.Strings(keys)
120+
121+
// Sort keys
122+
sort.Strings(keys)
123+
fmt.Println()
124+
fmt.Println("RESULTS START")
125+
fmt.Println()
126+
fmt.Printf("%-30s %15s %15s %15s %15s %15s\n", "TEST", "TOTAL TIME", "EXECUTIONS", "MIN", "MAX", "AVERAGE")
127+
for _, k := range keys {
128+
v := mapResults[k]
129+
fmt.Printf("%-30s %15v %15d %15v %15v %15v\n", k, time.Duration(v.TotalTime), v.Executions,
130+
v.MinTime, v.MaxTime, time.Duration(v.TotalTime/v.Executions))
131+
}
132+
fmt.Println()
133+
fmt.Println("RESULTS END")
134+
}
135+
80136
func errorAndExit(message string, err error) {
81137
fmt.Println(message, err)
82138
os.Exit(1)
@@ -101,23 +157,20 @@ func InitializeCoherence(ctx context.Context, address string) (Config, error) {
101157
return config, nil
102158
}
103159

160+
var (
161+
courses = []string{"C1", "C2", "C3", "C4"}
162+
countries = []string{"Australia", "USA", "Canada", "Mexico"}
163+
)
164+
104165
func populateCache(cache coherence.NamedMap[int, Student], count int) error {
105166
var (
106167
buffer = make(map[int]Student)
107168
err error
108-
courses = []string{"C1", "C2", "C3", "C4"}
109-
countries = []string{"Australia", "USA", "Canada", "Mexico"}
110169
batchSize = 10_000
111170
)
112171

113172
for i := 1; i <= count; i++ {
114-
buffer[i] = Student{
115-
ID: i,
116-
Name: fmt.Sprintf("student%d", i),
117-
Address: fmt.Sprintf("address%d", i),
118-
Country: randomize(countries),
119-
Course: randomize(courses),
120-
}
173+
buffer[i] = getRandomStudent(i)
121174
if i%batchSize == 0 {
122175
err = cache.PutAll(ctx, buffer)
123176
if err != nil {
@@ -133,9 +186,61 @@ func populateCache(cache coherence.NamedMap[int, Student], count int) error {
133186
return nil
134187
}
135188

189+
func getRandomStudent(i int) Student {
190+
return Student{
191+
ID: i,
192+
Name: fmt.Sprintf("student%d", i),
193+
Address: fmt.Sprintf("address%d", i),
194+
Country: randomize(countries),
195+
Course: randomize(courses),
196+
}
197+
}
198+
136199
func randomize(arr []string) string {
137200
if len(arr) == 0 {
138201
return ""
139202
}
140203
return arr[rnd.Intn(len(arr))]
141204
}
205+
206+
type testTimer struct {
207+
startTime time.Time
208+
endTime time.Time
209+
minDuration time.Duration
210+
maxDuration time.Duration
211+
currentStart time.Time
212+
count int64
213+
}
214+
215+
func (t *testTimer) Start() {
216+
t.currentStart = time.Now()
217+
}
218+
219+
func (t *testTimer) End() {
220+
duration := time.Since(t.currentStart)
221+
if duration < t.minDuration {
222+
t.minDuration = duration * time.Nanosecond
223+
}
224+
if duration > t.maxDuration {
225+
t.maxDuration = duration * time.Nanosecond
226+
}
227+
}
228+
229+
func (t *testTimer) Complete() *PerformanceResult {
230+
t.endTime = time.Now()
231+
return &PerformanceResult{
232+
Executions: t.count,
233+
TotalTime: t.endTime.Sub(t.startTime).Nanoseconds(),
234+
MaxTime: t.maxDuration,
235+
MinTime: t.minDuration,
236+
}
237+
}
238+
239+
func newTestTimer(count int64) *testTimer {
240+
return &testTimer{
241+
startTime: time.Now(),
242+
count: count,
243+
minDuration: time.Duration(10000) * time.Second,
244+
maxDuration: 0,
245+
}
246+
}

0 commit comments

Comments
 (0)