1// Copyright 2018 The etcd Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package lease 16 17import ( 18 "math/rand" 19 "os" 20 "testing" 21 "time" 22 23 "go.etcd.io/etcd/mvcc/backend" 24 "go.uber.org/zap" 25) 26 27func BenchmarkLessorGrant1000(b *testing.B) { benchmarkLessorGrant(1000, b) } 28func BenchmarkLessorGrant100000(b *testing.B) { benchmarkLessorGrant(100000, b) } 29 30func BenchmarkLessorRevoke1000(b *testing.B) { benchmarkLessorRevoke(1000, b) } 31func BenchmarkLessorRevoke100000(b *testing.B) { benchmarkLessorRevoke(100000, b) } 32 33func BenchmarkLessorRenew1000(b *testing.B) { benchmarkLessorRenew(1000, b) } 34func BenchmarkLessorRenew100000(b *testing.B) { benchmarkLessorRenew(100000, b) } 35 36// Use findExpired10000 replace findExpired1000, which takes too long. 37func BenchmarkLessorFindExpired10000(b *testing.B) { benchmarkLessorFindExpired(10000, b) } 38func BenchmarkLessorFindExpired100000(b *testing.B) { benchmarkLessorFindExpired(100000, b) } 39 40func init() { 41 rand.Seed(time.Now().UTC().UnixNano()) 42} 43 44const ( 45 // minTTL keep lease will not auto expire in benchmark 46 minTTL = 1000 47 // maxTTL control repeat probability of ttls 48 maxTTL = 2000 49) 50 51func randomTTL(n int, min, max int64) (out []int64) { 52 for i := 0; i < n; i++ { 53 out = append(out, rand.Int63n(max-min)+min) 54 } 55 return out 56} 57 58// demote lessor from being the primary, but don't change any lease's expiry 59func demote(le *lessor) { 60 le.mu.Lock() 61 defer le.mu.Unlock() 62 close(le.demotec) 63 le.demotec = nil 64} 65 66// return new lessor and tearDown to release resource 67func setUp() (le *lessor, tearDown func()) { 68 lg := zap.NewNop() 69 be, tmpPath := backend.NewDefaultTmpBackend() 70 // MinLeaseTTL is negative, so we can grant expired lease in benchmark. 71 // ExpiredLeasesRetryInterval should small, so benchmark of findExpired will recheck expired lease. 72 le = newLessor(lg, be, LessorConfig{MinLeaseTTL: -1000, ExpiredLeasesRetryInterval: 10 * time.Microsecond}) 73 le.SetRangeDeleter(func() TxnDelete { 74 ftd := &FakeTxnDelete{be.BatchTx()} 75 ftd.Lock() 76 return ftd 77 }) 78 le.Promote(0) 79 80 return le, func() { 81 le.Stop() 82 be.Close() 83 os.Remove(tmpPath) 84 } 85} 86 87func benchmarkLessorGrant(benchSize int, b *testing.B) { 88 ttls := randomTTL(benchSize, minTTL, maxTTL) 89 90 var le *lessor 91 var tearDown func() 92 93 b.ResetTimer() 94 for i := 0; i < b.N; { 95 b.StopTimer() 96 if tearDown != nil { 97 tearDown() 98 tearDown = nil 99 } 100 le, tearDown = setUp() 101 b.StartTimer() 102 103 for j := 1; j <= benchSize; j++ { 104 le.Grant(LeaseID(j), ttls[j-1]) 105 } 106 i += benchSize 107 } 108 b.StopTimer() 109 110 if tearDown != nil { 111 tearDown() 112 } 113} 114 115func benchmarkLessorRevoke(benchSize int, b *testing.B) { 116 ttls := randomTTL(benchSize, minTTL, maxTTL) 117 118 var le *lessor 119 var tearDown func() 120 b.ResetTimer() 121 for i := 0; i < b.N; i++ { 122 b.StopTimer() 123 if tearDown != nil { 124 tearDown() 125 tearDown = nil 126 } 127 le, tearDown = setUp() 128 for j := 1; j <= benchSize; j++ { 129 le.Grant(LeaseID(j), ttls[j-1]) 130 } 131 b.StartTimer() 132 133 for j := 1; j <= benchSize; j++ { 134 le.Revoke(LeaseID(j)) 135 } 136 i += benchSize 137 } 138 b.StopTimer() 139 140 if tearDown != nil { 141 tearDown() 142 } 143} 144 145func benchmarkLessorRenew(benchSize int, b *testing.B) { 146 ttls := randomTTL(benchSize, minTTL, maxTTL) 147 148 var le *lessor 149 var tearDown func() 150 151 b.ResetTimer() 152 for i := 0; i < b.N; { 153 b.StopTimer() 154 if tearDown != nil { 155 tearDown() 156 tearDown = nil 157 } 158 le, tearDown = setUp() 159 for j := 1; j <= benchSize; j++ { 160 le.Grant(LeaseID(j), ttls[j-1]) 161 } 162 b.StartTimer() 163 164 for j := 1; j <= benchSize; j++ { 165 le.Renew(LeaseID(j)) 166 } 167 i += benchSize 168 } 169 b.StopTimer() 170 171 if tearDown != nil { 172 tearDown() 173 } 174} 175 176func benchmarkLessorFindExpired(benchSize int, b *testing.B) { 177 // 50% lease are expired. 178 ttls := randomTTL(benchSize, -500, 500) 179 findExpiredLimit := 50 180 181 var le *lessor 182 var tearDown func() 183 184 b.ResetTimer() 185 for i := 0; i < b.N; { 186 b.StopTimer() 187 if tearDown != nil { 188 tearDown() 189 tearDown = nil 190 } 191 le, tearDown = setUp() 192 for j := 1; j <= benchSize; j++ { 193 le.Grant(LeaseID(j), ttls[j-1]) 194 } 195 // lessor's runLoop should not call findExpired 196 demote(le) 197 b.StartTimer() 198 199 // refresh fixture after pop all expired lease 200 for ; ; i++ { 201 le.mu.Lock() 202 ls := le.findExpiredLeases(findExpiredLimit) 203 if len(ls) == 0 { 204 break 205 } 206 le.mu.Unlock() 207 208 // simulation: revoke lease after expired 209 b.StopTimer() 210 for _, lease := range ls { 211 le.Revoke(lease.ID) 212 } 213 b.StartTimer() 214 } 215 } 216 b.StopTimer() 217 218 if tearDown != nil { 219 tearDown() 220 } 221} 222