go.temporal.io/server@v1.23.0/common/locks/id_mutex_test.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package locks 26 27 import ( 28 "sync" 29 "testing" 30 31 "github.com/dgryski/go-farm" 32 "github.com/stretchr/testify/suite" 33 ) 34 35 type ( 36 idMutexSuite struct { 37 suite.Suite 38 39 numShard uint32 40 idMutex IDMutex 41 } 42 43 testIdentifier struct { 44 A string 45 B string 46 C string 47 } 48 ) 49 50 func BenchmarkGolangMutex(b *testing.B) { 51 lock := &sync.Mutex{} 52 for i := 0; i < b.N; i++ { 53 lock.Lock() 54 func() {}() 55 lock.Unlock() 56 } 57 } 58 59 func BenchmarkIDMutex_String(b *testing.B) { 60 identifier := "random string" 61 idLock := NewIDMutex(32, func(key interface{}) uint32 { 62 id, ok := key.(string) 63 if !ok { 64 return 0 65 } 66 return farm.Fingerprint32([]byte(id)) 67 }) 68 69 for i := 0; i < b.N; i++ { 70 idLock.LockID(identifier) 71 idLock.UnlockID(identifier) 72 } 73 } 74 75 func BenchmarkIDMutex_Struct(b *testing.B) { 76 identifier := testIdentifier{ 77 A: "some random A", 78 B: "some random B", 79 C: "some random C", 80 } 81 idLock := NewIDMutex(32, func(key interface{}) uint32 { 82 id, ok := key.(testIdentifier) 83 if !ok { 84 return 0 85 } 86 return farm.Fingerprint32([]byte(id.A)) 87 }) 88 89 for i := 0; i < b.N; i++ { 90 idLock.LockID(identifier) 91 idLock.UnlockID(identifier) 92 } 93 } 94 95 func BenchmarkIDMutex_StringConcurrent(b *testing.B) { 96 identifier := "random string" 97 idLock := NewIDMutex(32, func(key interface{}) uint32 { 98 id, ok := key.(string) 99 if !ok { 100 return 0 101 } 102 return farm.Fingerprint32([]byte(id)) 103 }) 104 105 for i := 0; i < b.N; i++ { 106 counter := 0 107 iteration := 2000 108 109 waitGroupBegin := &sync.WaitGroup{} 110 waitGroupBegin.Add(1) 111 112 waitGroupEnd := &sync.WaitGroup{} 113 waitGroupEnd.Add(iteration) 114 115 fn := func() { 116 waitGroupBegin.Wait() 117 118 idLock.LockID(identifier) 119 counter++ 120 idLock.UnlockID(identifier) 121 122 waitGroupEnd.Done() 123 } 124 125 for i := 0; i < iteration; i++ { 126 go fn() 127 } 128 waitGroupBegin.Done() 129 waitGroupEnd.Wait() 130 } 131 } 132 133 func TestIDMutexSuite(t *testing.T) { 134 s := new(idMutexSuite) 135 suite.Run(t, s) 136 } 137 138 func (s *idMutexSuite) SetupSuite() { 139 } 140 141 func (s *idMutexSuite) TearDownSuite() { 142 143 } 144 145 func (s *idMutexSuite) SetupTest() { 146 s.numShard = 32 147 s.idMutex = NewIDMutex(s.numShard, func(key interface{}) uint32 { 148 id, ok := key.(string) 149 if !ok { 150 return 0 151 } 152 return farm.Fingerprint32([]byte(id)) 153 }) 154 } 155 156 func (s *idMutexSuite) TearDownTest() { 157 158 } 159 160 func (s *idMutexSuite) TestLockOnDiffIDs() { 161 identifier1 := "some random identifier 1" 162 identifier2 := "some random identifier 2" 163 164 s.idMutex.LockID(identifier1) 165 s.idMutex.LockID(identifier2) 166 s.idMutex.UnlockID(identifier2) 167 s.idMutex.UnlockID(identifier1) 168 169 s.idMutex.LockID(identifier1) 170 s.idMutex.LockID(identifier2) 171 s.idMutex.UnlockID(identifier1) 172 s.idMutex.UnlockID(identifier2) 173 } 174 175 func (s *idMutexSuite) TestLockOnSameID() { 176 count := 0 177 identifier := "some random identifier" 178 179 waitGroupBegin := &sync.WaitGroup{} 180 waitGroupBegin.Add(1) 181 182 waitGroupEnd := &sync.WaitGroup{} 183 waitGroupEnd.Add(1) 184 185 s.idMutex.LockID(identifier) 186 go func() { 187 waitGroupBegin.Done() 188 s.idMutex.LockID(identifier) 189 s.Equal(1, count) 190 s.idMutex.UnlockID(identifier) 191 waitGroupEnd.Done() 192 }() 193 waitGroupBegin.Wait() 194 count = 1 195 s.idMutex.UnlockID(identifier) 196 waitGroupEnd.Wait() 197 } 198 199 func (s *idMutexSuite) TestConcurrentAccess() { 200 counter := 0 201 identifier := "some random identifier" 202 iteration := 2000 203 204 waitGroupBegin := &sync.WaitGroup{} 205 waitGroupBegin.Add(1) 206 207 waitGroupEnd := &sync.WaitGroup{} 208 waitGroupEnd.Add(iteration) 209 210 fn := func() { 211 waitGroupBegin.Wait() 212 213 s.idMutex.LockID(identifier) 214 counter++ 215 s.idMutex.UnlockID(identifier) 216 217 waitGroupEnd.Done() 218 } 219 220 for i := 0; i < iteration; i++ { 221 go fn() 222 } 223 waitGroupBegin.Done() 224 waitGroupEnd.Wait() 225 226 s.Equal(iteration, counter) 227 impl := s.idMutex.(*idMutexImpl) 228 for i := uint32(0); i < s.numShard; i++ { 229 s.Equal(0, len(impl.shards[i].mutexInfos)) 230 } 231 }