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 }