-
Notifications
You must be signed in to change notification settings - Fork 0
/
hlc_test.go
162 lines (138 loc) · 3.93 KB
/
hlc_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2023-Present Couchbase, Inc.
//
// Use of this software is governed by the Business Source License included
// in the file licenses/BSL-Couchbase.txt. As of the Change Date specified
// in that file, in accordance with the Business Source License, use of this
// software will be governed by the Apache License, Version 2.0, included in
// the file licenses/APL2.txt.
package rosmar
import (
"fmt"
"sync"
"testing"
sgbucket "github.com/couchbase/sg-bucket"
"github.com/stretchr/testify/require"
)
func TestHybridLogicalClockNow(t *testing.T) {
clock := hybridLogicalClock{clock: &systemClock{}}
timestamp1 := clock.Now()
timestamp2 := clock.Now()
require.Greater(t, timestamp2, timestamp1)
}
func generateTimestamps(wg *sync.WaitGroup, clock *hybridLogicalClock, n int, result chan []timestamp) {
defer wg.Done()
timestamps := make([]timestamp, n)
for i := 0; i < n; i++ {
timestamps[i] = clock.Now()
}
result <- timestamps
}
func TestHLCNowConcurrent(t *testing.T) {
clock := hybridLogicalClock{clock: &systemClock{}}
goroutines := 100
timestampCount := 100
wg := sync.WaitGroup{}
results := make(chan []timestamp)
for i := 0; i < goroutines; i++ {
wg.Add(1)
go generateTimestamps(&wg, &clock, timestampCount, results)
}
doneChan := make(chan struct{})
go func() {
wg.Wait()
doneChan <- struct{}{}
}()
allTimestamps := make([]timestamp, 0, goroutines*timestampCount)
loop:
for {
select {
case timestamps := <-results:
allTimestamps = append(allTimestamps, timestamps...)
case <-doneChan:
break loop
}
}
uniqueTimestamps := make(map[timestamp]struct{})
for _, timestamp := range allTimestamps {
if _, ok := uniqueTimestamps[timestamp]; ok {
t.Errorf("Timestamp %d is not unique", timestamp)
}
uniqueTimestamps[timestamp] = struct{}{}
}
}
type fakeClock struct {
time uint64
}
func (c *fakeClock) getTime() uint64 {
return c.time
}
func TestHLCReverseTime(t *testing.T) {
clock := &fakeClock{}
hlc := hybridLogicalClock{clock: clock}
startTime := uint64(1000000) // 1 second
clock.time = startTime
require.Equal(t, timestamp(0xf0000), hlc.Now())
require.Equal(t, timestamp(0xf0001), hlc.Now())
// reverse time no counter
clock.time = 0
require.Equal(t, timestamp(0xf0002), hlc.Now())
// reset time to normal
clock.time = startTime
require.Equal(t, timestamp(0xf0003), hlc.Now())
// reverse time again
clock.time = 1
require.Equal(t, timestamp(0xf0004), hlc.Now())
// jump to a value we had previously
clock.time = startTime * 2
require.Equal(t, timestamp(0x1e0000), hlc.Now())
require.Equal(t, timestamp(0x1e0001), hlc.Now())
// continue forward
clock.time *= 2
require.Equal(t, timestamp(0x3d0000), hlc.Now())
}
func TestHLCCrossBucket(t *testing.T) {
goroutines := 10
documentCount := 10
collection1 := makeTestBucketWithName(t, "bucket1").DefaultDataStore()
collection2 := makeTestBucketWithName(t, "bucket2").DefaultDataStore()
wg := sync.WaitGroup{}
results := make(chan []uint64)
createDocuments := func(goroutineIdx int, collection sgbucket.DataStore) {
defer wg.Done()
casValues := make([]uint64, documentCount)
for i := 0; i < documentCount; i++ {
cas, err := collection.WriteCas(fmt.Sprintf("key_%d_%d", goroutineIdx, i), 0, 0, []byte(" World"), sgbucket.AddOnly)
require.NoError(t, err)
casValues[i] = cas
}
results <- casValues
}
for i := 0; i < goroutines; i++ {
for _, collection := range []sgbucket.DataStore{collection1, collection2} {
wg.Add(1)
go createDocuments(i, collection)
}
}
doneChan := make(chan struct{})
go func() {
wg.Wait()
doneChan <- struct{}{}
}()
allCas := make([]uint64, 0, goroutines*documentCount)
loop:
for {
select {
case casValues := <-results:
allCas = append(allCas, casValues...)
case <-doneChan:
break loop
}
}
uniqueCas := make(map[uint64]struct{})
for _, cas := range allCas {
if _, ok := uniqueCas[cas]; ok {
t.Errorf("cas %d is not unique", cas)
}
uniqueCas[cas] = struct{}{}
}
}