vitess.io/vitess@v0.16.2/go/vt/topo/stats_conn.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  	"time"
    22  
    23  	"vitess.io/vitess/go/stats"
    24  	"vitess.io/vitess/go/vt/proto/vtrpc"
    25  	"vitess.io/vitess/go/vt/vterrors"
    26  )
    27  
    28  var _ Conn = (*StatsConn)(nil)
    29  
    30  var (
    31  	topoStatsConnTimings = stats.NewMultiTimings(
    32  		"TopologyConnOperations",
    33  		"TopologyConnOperations timings",
    34  		[]string{"Operation", "Cell"})
    35  
    36  	topoStatsConnErrors = stats.NewCountersWithMultiLabels(
    37  		"TopologyConnErrors",
    38  		"TopologyConnErrors errors per operation",
    39  		[]string{"Operation", "Cell"})
    40  )
    41  
    42  const readOnlyErrorStrFormat = "cannot perform %s on %s as the topology server connection is read-only"
    43  
    44  // The StatsConn is a wrapper for a Conn that emits stats for every operation
    45  type StatsConn struct {
    46  	cell     string
    47  	conn     Conn
    48  	readOnly bool
    49  }
    50  
    51  // NewStatsConn returns a StatsConn
    52  func NewStatsConn(cell string, conn Conn) *StatsConn {
    53  	return &StatsConn{
    54  		cell:     cell,
    55  		conn:     conn,
    56  		readOnly: false,
    57  	}
    58  }
    59  
    60  // ListDir is part of the Conn interface
    61  func (st *StatsConn) ListDir(ctx context.Context, dirPath string, full bool) ([]DirEntry, error) {
    62  	startTime := time.Now()
    63  	statsKey := []string{"ListDir", st.cell}
    64  	defer topoStatsConnTimings.Record(statsKey, startTime)
    65  	res, err := st.conn.ListDir(ctx, dirPath, full)
    66  	if err != nil {
    67  		topoStatsConnErrors.Add(statsKey, int64(1))
    68  		return res, err
    69  	}
    70  	return res, err
    71  }
    72  
    73  // Create is part of the Conn interface
    74  func (st *StatsConn) Create(ctx context.Context, filePath string, contents []byte) (Version, error) {
    75  	statsKey := []string{"Create", st.cell}
    76  	if st.readOnly {
    77  		return nil, vterrors.Errorf(vtrpc.Code_READ_ONLY, readOnlyErrorStrFormat, statsKey[0], filePath)
    78  	}
    79  	startTime := time.Now()
    80  	defer topoStatsConnTimings.Record(statsKey, startTime)
    81  	res, err := st.conn.Create(ctx, filePath, contents)
    82  	if err != nil {
    83  		topoStatsConnErrors.Add(statsKey, int64(1))
    84  		return res, err
    85  	}
    86  	return res, err
    87  }
    88  
    89  // Update is part of the Conn interface
    90  func (st *StatsConn) Update(ctx context.Context, filePath string, contents []byte, version Version) (Version, error) {
    91  	statsKey := []string{"Update", st.cell}
    92  	if st.readOnly {
    93  		return nil, vterrors.Errorf(vtrpc.Code_READ_ONLY, readOnlyErrorStrFormat, statsKey[0], filePath)
    94  	}
    95  	startTime := time.Now()
    96  	defer topoStatsConnTimings.Record(statsKey, startTime)
    97  	res, err := st.conn.Update(ctx, filePath, contents, version)
    98  	if err != nil {
    99  		topoStatsConnErrors.Add(statsKey, int64(1))
   100  		return res, err
   101  	}
   102  	return res, err
   103  }
   104  
   105  // Get is part of the Conn interface
   106  func (st *StatsConn) Get(ctx context.Context, filePath string) ([]byte, Version, error) {
   107  	startTime := time.Now()
   108  	statsKey := []string{"Get", st.cell}
   109  	defer topoStatsConnTimings.Record(statsKey, startTime)
   110  	bytes, version, err := st.conn.Get(ctx, filePath)
   111  	if err != nil {
   112  		topoStatsConnErrors.Add(statsKey, int64(1))
   113  		return bytes, version, err
   114  	}
   115  	return bytes, version, err
   116  }
   117  
   118  // List is part of the Conn interface
   119  func (st *StatsConn) List(ctx context.Context, filePathPrefix string) ([]KVInfo, error) {
   120  	startTime := time.Now()
   121  	statsKey := []string{"List", st.cell}
   122  	defer topoStatsConnTimings.Record(statsKey, startTime)
   123  	bytes, err := st.conn.List(ctx, filePathPrefix)
   124  	if err != nil {
   125  		topoStatsConnErrors.Add(statsKey, int64(1))
   126  		return bytes, err
   127  	}
   128  	return bytes, err
   129  }
   130  
   131  // Delete is part of the Conn interface
   132  func (st *StatsConn) Delete(ctx context.Context, filePath string, version Version) error {
   133  	statsKey := []string{"Delete", st.cell}
   134  	if st.readOnly {
   135  		return vterrors.Errorf(vtrpc.Code_READ_ONLY, readOnlyErrorStrFormat, statsKey[0], filePath)
   136  	}
   137  	startTime := time.Now()
   138  	defer topoStatsConnTimings.Record(statsKey, startTime)
   139  	err := st.conn.Delete(ctx, filePath, version)
   140  	if err != nil {
   141  		topoStatsConnErrors.Add(statsKey, int64(1))
   142  		return err
   143  	}
   144  	return err
   145  }
   146  
   147  // Lock is part of the Conn interface
   148  func (st *StatsConn) Lock(ctx context.Context, dirPath, contents string) (LockDescriptor, error) {
   149  	return st.internalLock(ctx, dirPath, contents, true)
   150  }
   151  
   152  // TryLock is part of the topo.Conn interface. Its implementation is same as Lock
   153  func (st *StatsConn) TryLock(ctx context.Context, dirPath, contents string) (LockDescriptor, error) {
   154  	return st.internalLock(ctx, dirPath, contents, false)
   155  }
   156  
   157  // TryLock is part of the topo.Conn interface. Its implementation is same as Lock
   158  func (st *StatsConn) internalLock(ctx context.Context, dirPath, contents string, isBlocking bool) (LockDescriptor, error) {
   159  	statsKey := []string{"Lock", st.cell}
   160  	if st.readOnly {
   161  		return nil, vterrors.Errorf(vtrpc.Code_READ_ONLY, readOnlyErrorStrFormat, statsKey[0], dirPath)
   162  	}
   163  	startTime := time.Now()
   164  	defer topoStatsConnTimings.Record(statsKey, startTime)
   165  	var res LockDescriptor
   166  	var err error
   167  	if isBlocking {
   168  		res, err = st.conn.Lock(ctx, dirPath, contents)
   169  	} else {
   170  		res, err = st.conn.TryLock(ctx, dirPath, contents)
   171  	}
   172  	if err != nil {
   173  		topoStatsConnErrors.Add(statsKey, int64(1))
   174  		return res, err
   175  	}
   176  	return res, err
   177  }
   178  
   179  // Watch is part of the Conn interface
   180  func (st *StatsConn) Watch(ctx context.Context, filePath string) (current *WatchData, changes <-chan *WatchData, err error) {
   181  	startTime := time.Now()
   182  	statsKey := []string{"Watch", st.cell}
   183  	defer topoStatsConnTimings.Record(statsKey, startTime)
   184  	return st.conn.Watch(ctx, filePath)
   185  }
   186  
   187  func (st *StatsConn) WatchRecursive(ctx context.Context, path string) ([]*WatchDataRecursive, <-chan *WatchDataRecursive, error) {
   188  	startTime := time.Now()
   189  	statsKey := []string{"WatchRecursive", st.cell}
   190  	defer topoStatsConnTimings.Record(statsKey, startTime)
   191  	return st.conn.WatchRecursive(ctx, path)
   192  }
   193  
   194  // NewLeaderParticipation is part of the Conn interface
   195  func (st *StatsConn) NewLeaderParticipation(name, id string) (LeaderParticipation, error) {
   196  	startTime := time.Now()
   197  	statsKey := []string{"NewLeaderParticipation", st.cell}
   198  	defer topoStatsConnTimings.Record(statsKey, startTime)
   199  	res, err := st.conn.NewLeaderParticipation(name, id)
   200  	if err != nil {
   201  		topoStatsConnErrors.Add(statsKey, int64(1))
   202  		return res, err
   203  	}
   204  	return res, err
   205  }
   206  
   207  // Close is part of the Conn interface
   208  func (st *StatsConn) Close() {
   209  	startTime := time.Now()
   210  	statsKey := []string{"Close", st.cell}
   211  	defer topoStatsConnTimings.Record(statsKey, startTime)
   212  	st.conn.Close()
   213  }
   214  
   215  // SetReadOnly with true prevents any write operations from being made on the topo connection
   216  func (st *StatsConn) SetReadOnly(readOnly bool) {
   217  	st.readOnly = readOnly
   218  }
   219  
   220  // IsReadOnly allows you to check the access type for the topo connection
   221  func (st *StatsConn) IsReadOnly() bool {
   222  	return st.readOnly
   223  }