vitess.io/vitess@v0.16.2/go/vt/topo/stats_conn_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package topo 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 24 "vitess.io/vitess/go/vt/proto/vtrpc" 25 "vitess.io/vitess/go/vt/vterrors" 26 ) 27 28 // The fakeConn is a wrapper for a Conn that emits stats for every operation 29 type fakeConn struct { 30 v Version 31 readOnly bool 32 } 33 34 // ListDir is part of the Conn interface 35 func (st *fakeConn) ListDir(ctx context.Context, dirPath string, full bool) (res []DirEntry, err error) { 36 if dirPath == "error" { 37 return res, fmt.Errorf("Dummy error") 38 39 } 40 return res, err 41 } 42 43 // Create is part of the Conn interface 44 func (st *fakeConn) Create(ctx context.Context, filePath string, contents []byte) (ver Version, err error) { 45 if st.readOnly { 46 return nil, vterrors.Errorf(vtrpc.Code_READ_ONLY, "topo server connection is read-only") 47 } 48 if filePath == "error" { 49 return ver, fmt.Errorf("Dummy error") 50 51 } 52 return ver, err 53 } 54 55 // Update is part of the Conn interface 56 func (st *fakeConn) Update(ctx context.Context, filePath string, contents []byte, version Version) (ver Version, err error) { 57 if st.readOnly { 58 return nil, vterrors.Errorf(vtrpc.Code_READ_ONLY, "topo server connection is read-only") 59 } 60 if filePath == "error" { 61 return ver, fmt.Errorf("Dummy error") 62 63 } 64 return ver, err 65 } 66 67 // Get is part of the Conn interface 68 func (st *fakeConn) Get(ctx context.Context, filePath string) (bytes []byte, ver Version, err error) { 69 if filePath == "error" { 70 return bytes, ver, fmt.Errorf("Dummy error") 71 72 } 73 return bytes, ver, err 74 } 75 76 // List is part of the Conn interface 77 func (st *fakeConn) List(ctx context.Context, filePathPrefix string) (bytes []KVInfo, err error) { 78 if filePathPrefix == "error" { 79 return bytes, fmt.Errorf("Dummy error") 80 } 81 return bytes, err 82 } 83 84 // Delete is part of the Conn interface 85 func (st *fakeConn) Delete(ctx context.Context, filePath string, version Version) (err error) { 86 if st.readOnly { 87 return vterrors.Errorf(vtrpc.Code_READ_ONLY, "topo server connection is read-only") 88 } 89 if filePath == "error" { 90 return fmt.Errorf("dummy error") 91 } 92 return err 93 } 94 95 // Lock is part of the Conn interface 96 func (st *fakeConn) Lock(ctx context.Context, dirPath, contents string) (lock LockDescriptor, err error) { 97 if st.readOnly { 98 return nil, vterrors.Errorf(vtrpc.Code_READ_ONLY, "topo server connection is read-only") 99 } 100 if dirPath == "error" { 101 return lock, fmt.Errorf("dummy error") 102 103 } 104 return lock, err 105 } 106 107 // TryLock is part of the topo.Conn interface. 108 // As of today it provides same functionality as Lock 109 func (st *fakeConn) TryLock(ctx context.Context, dirPath, contents string) (lock LockDescriptor, err error) { 110 if st.readOnly { 111 return nil, vterrors.Errorf(vtrpc.Code_READ_ONLY, "topo server connection is read-only") 112 } 113 if dirPath == "error" { 114 return lock, fmt.Errorf("dummy error") 115 116 } 117 return lock, err 118 } 119 120 // Watch is part of the Conn interface 121 func (st *fakeConn) Watch(ctx context.Context, filePath string) (current *WatchData, changes <-chan *WatchData, err error) { 122 return current, changes, err 123 } 124 125 // WatchRecursive is part of the Conn interface 126 func (st *fakeConn) WatchRecursive(ctx context.Context, path string) (current []*WatchDataRecursive, changes <-chan *WatchDataRecursive, err error) { 127 return current, changes, err 128 } 129 130 // NewLeaderParticipation is part of the Conn interface 131 func (st *fakeConn) NewLeaderParticipation(name, id string) (mp LeaderParticipation, err error) { 132 if name == "error" { 133 return mp, fmt.Errorf("dummy error") 134 135 } 136 return mp, err 137 } 138 139 // Close is part of the Conn interface 140 func (st *fakeConn) Close() { 141 } 142 143 // SetReadOnly with true prevents any write operations from being made on the topo connection 144 func (st *fakeConn) SetReadOnly(readOnly bool) { 145 st.readOnly = readOnly 146 } 147 148 // IsReadOnly allows you to check the access type for the topo connection 149 func (st *fakeConn) IsReadOnly() bool { 150 return st.readOnly 151 } 152 153 // TestStatsConnTopoListDir emits stats on ListDir 154 func TestStatsConnTopoListDir(t *testing.T) { 155 conn := &fakeConn{} 156 statsConn := NewStatsConn("global", conn) 157 ctx := context.Background() 158 159 statsConn.ListDir(ctx, "", true) 160 timingCounts := topoStatsConnTimings.Counts()["ListDir.global"] 161 if got, want := timingCounts, int64(1); got != want { 162 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 163 } 164 165 // error is zero before getting an error 166 errorCount := topoStatsConnErrors.Counts()["ListDir.global"] 167 if got, want := errorCount, int64(0); got != want { 168 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 169 } 170 171 statsConn.ListDir(ctx, "error", true) 172 173 // error stats gets emitted 174 errorCount = topoStatsConnErrors.Counts()["ListDir.global"] 175 if got, want := errorCount, int64(1); got != want { 176 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 177 } 178 } 179 180 // TestStatsConnTopoCreate emits stats on Create 181 func TestStatsConnTopoCreate(t *testing.T) { 182 conn := &fakeConn{} 183 statsConn := NewStatsConn("global", conn) 184 ctx := context.Background() 185 186 statsConn.Create(ctx, "", []byte{}) 187 timingCounts := topoStatsConnTimings.Counts()["Create.global"] 188 if got, want := timingCounts, int64(1); got != want { 189 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 190 } 191 192 // error is zero before getting an error 193 errorCount := topoStatsConnErrors.Counts()["Create.global"] 194 if got, want := errorCount, int64(0); got != want { 195 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 196 } 197 198 statsConn.Create(ctx, "error", []byte{}) 199 200 // error stats gets emitted 201 errorCount = topoStatsConnErrors.Counts()["Create.global"] 202 if got, want := errorCount, int64(1); got != want { 203 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 204 } 205 } 206 207 // TestStatsConnTopoUpdate emits stats on Update 208 func TestStatsConnTopoUpdate(t *testing.T) { 209 conn := &fakeConn{} 210 statsConn := NewStatsConn("global", conn) 211 ctx := context.Background() 212 213 statsConn.Update(ctx, "", []byte{}, conn.v) 214 timingCounts := topoStatsConnTimings.Counts()["Update.global"] 215 if got, want := timingCounts, int64(1); got != want { 216 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 217 } 218 219 // error is zero before getting an error 220 errorCount := topoStatsConnErrors.Counts()["Update.global"] 221 if got, want := errorCount, int64(0); got != want { 222 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 223 } 224 225 statsConn.Update(ctx, "error", []byte{}, conn.v) 226 227 // error stats gets emitted 228 errorCount = topoStatsConnErrors.Counts()["Update.global"] 229 if got, want := errorCount, int64(1); got != want { 230 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 231 } 232 } 233 234 // TestStatsConnTopoGet emits stats on Get 235 func TestStatsConnTopoGet(t *testing.T) { 236 conn := &fakeConn{} 237 statsConn := NewStatsConn("global", conn) 238 ctx := context.Background() 239 240 statsConn.Get(ctx, "") 241 timingCounts := topoStatsConnTimings.Counts()["Get.global"] 242 if got, want := timingCounts, int64(1); got != want { 243 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 244 } 245 246 // error is zero before getting an error 247 errorCount := topoStatsConnErrors.Counts()["Get.global"] 248 if got, want := errorCount, int64(0); got != want { 249 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 250 } 251 252 statsConn.Get(ctx, "error") 253 254 // error stats gets emitted 255 errorCount = topoStatsConnErrors.Counts()["Get.global"] 256 if got, want := errorCount, int64(1); got != want { 257 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 258 } 259 } 260 261 // TestStatsConnTopoDelete emits stats on Delete 262 func TestStatsConnTopoDelete(t *testing.T) { 263 conn := &fakeConn{} 264 statsConn := NewStatsConn("global", conn) 265 ctx := context.Background() 266 267 statsConn.Delete(ctx, "", conn.v) 268 timingCounts := topoStatsConnTimings.Counts()["Delete.global"] 269 if got, want := timingCounts, int64(1); got != want { 270 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 271 } 272 273 // error is zero before getting an error 274 errorCount := topoStatsConnErrors.Counts()["Delete.global"] 275 if got, want := errorCount, int64(0); got != want { 276 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 277 } 278 279 statsConn.Delete(ctx, "error", conn.v) 280 281 // error stats gets emitted 282 errorCount = topoStatsConnErrors.Counts()["Delete.global"] 283 if got, want := errorCount, int64(1); got != want { 284 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 285 } 286 } 287 288 // TestStatsConnTopoLock emits stats on Lock 289 func TestStatsConnTopoLock(t *testing.T) { 290 conn := &fakeConn{} 291 statsConn := NewStatsConn("global", conn) 292 ctx := context.Background() 293 294 statsConn.Lock(ctx, "", "") 295 timingCounts := topoStatsConnTimings.Counts()["Lock.global"] 296 if got, want := timingCounts, int64(1); got != want { 297 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 298 } 299 300 // error is zero before getting an error 301 errorCount := topoStatsConnErrors.Counts()["Lock.global"] 302 if got, want := errorCount, int64(0); got != want { 303 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 304 } 305 306 statsConn.Lock(ctx, "error", "") 307 308 // error stats gets emitted 309 errorCount = topoStatsConnErrors.Counts()["Lock.global"] 310 if got, want := errorCount, int64(1); got != want { 311 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 312 } 313 } 314 315 // TestStatsConnTopoWatch emits stats on Watch 316 func TestStatsConnTopoWatch(t *testing.T) { 317 conn := &fakeConn{} 318 statsConn := NewStatsConn("global", conn) 319 ctx := context.Background() 320 321 statsConn.Watch(ctx, "") 322 timingCounts := topoStatsConnTimings.Counts()["Watch.global"] 323 if got, want := timingCounts, int64(1); got != want { 324 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 325 } 326 327 } 328 329 // TestStatsConnTopoNewLeaderParticipation emits stats on NewLeaderParticipation 330 func TestStatsConnTopoNewLeaderParticipation(t *testing.T) { 331 conn := &fakeConn{} 332 statsConn := NewStatsConn("global", conn) 333 334 _, _ = statsConn.NewLeaderParticipation("", "") 335 timingCounts := topoStatsConnTimings.Counts()["NewLeaderParticipation.global"] 336 if got, want := timingCounts, int64(1); got != want { 337 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 338 } 339 340 // error is zero before getting an error 341 errorCount := topoStatsConnErrors.Counts()["NewLeaderParticipation.global"] 342 if got, want := errorCount, int64(0); got != want { 343 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 344 } 345 346 _, _ = statsConn.NewLeaderParticipation("error", "") 347 348 // error stats gets emitted 349 errorCount = topoStatsConnErrors.Counts()["NewLeaderParticipation.global"] 350 if got, want := errorCount, int64(1); got != want { 351 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 352 } 353 } 354 355 // TestStatsConnTopoClose emits stats on Close 356 func TestStatsConnTopoClose(t *testing.T) { 357 conn := &fakeConn{} 358 statsConn := NewStatsConn("global", conn) 359 360 statsConn.Close() 361 timingCounts := topoStatsConnTimings.Counts()["Close.global"] 362 if got, want := timingCounts, int64(1); got != want { 363 t.Errorf("stats were not properly recorded: got = %d, want = %d", got, want) 364 } 365 }