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  }