github.com/MetalBlockchain/metalgo@v1.11.9/database/meterdb/db.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package meterdb 5 6 import ( 7 "context" 8 "errors" 9 "time" 10 11 "github.com/prometheus/client_golang/prometheus" 12 13 "github.com/MetalBlockchain/metalgo/database" 14 ) 15 16 const methodLabel = "method" 17 18 var ( 19 _ database.Database = (*Database)(nil) 20 _ database.Batch = (*batch)(nil) 21 _ database.Iterator = (*iterator)(nil) 22 23 methodLabels = []string{methodLabel} 24 hasLabel = prometheus.Labels{ 25 methodLabel: "has", 26 } 27 getLabel = prometheus.Labels{ 28 methodLabel: "get", 29 } 30 putLabel = prometheus.Labels{ 31 methodLabel: "put", 32 } 33 deleteLabel = prometheus.Labels{ 34 methodLabel: "delete", 35 } 36 newBatchLabel = prometheus.Labels{ 37 methodLabel: "new_batch", 38 } 39 newIteratorLabel = prometheus.Labels{ 40 methodLabel: "new_iterator", 41 } 42 compactLabel = prometheus.Labels{ 43 methodLabel: "compact", 44 } 45 closeLabel = prometheus.Labels{ 46 methodLabel: "close", 47 } 48 healthCheckLabel = prometheus.Labels{ 49 methodLabel: "health_check", 50 } 51 batchPutLabel = prometheus.Labels{ 52 methodLabel: "batch_put", 53 } 54 batchDeleteLabel = prometheus.Labels{ 55 methodLabel: "batch_delete", 56 } 57 batchSizeLabel = prometheus.Labels{ 58 methodLabel: "batch_size", 59 } 60 batchWriteLabel = prometheus.Labels{ 61 methodLabel: "batch_write", 62 } 63 batchResetLabel = prometheus.Labels{ 64 methodLabel: "batch_reset", 65 } 66 batchReplayLabel = prometheus.Labels{ 67 methodLabel: "batch_replay", 68 } 69 batchInnerLabel = prometheus.Labels{ 70 methodLabel: "batch_inner", 71 } 72 iteratorNextLabel = prometheus.Labels{ 73 methodLabel: "iterator_next", 74 } 75 iteratorErrorLabel = prometheus.Labels{ 76 methodLabel: "iterator_error", 77 } 78 iteratorKeyLabel = prometheus.Labels{ 79 methodLabel: "iterator_key", 80 } 81 iteratorValueLabel = prometheus.Labels{ 82 methodLabel: "iterator_value", 83 } 84 iteratorReleaseLabel = prometheus.Labels{ 85 methodLabel: "iterator_release", 86 } 87 ) 88 89 // Database tracks the amount of time each operation takes and how many bytes 90 // are read/written to the underlying database instance. 91 type Database struct { 92 db database.Database 93 94 calls *prometheus.CounterVec 95 duration *prometheus.GaugeVec 96 size *prometheus.CounterVec 97 } 98 99 // New returns a new database with added metrics 100 func New( 101 reg prometheus.Registerer, 102 db database.Database, 103 ) (*Database, error) { 104 meterDB := &Database{ 105 db: db, 106 calls: prometheus.NewCounterVec( 107 prometheus.CounterOpts{ 108 Name: "calls", 109 Help: "number of calls to the database", 110 }, 111 methodLabels, 112 ), 113 duration: prometheus.NewGaugeVec( 114 prometheus.GaugeOpts{ 115 Name: "duration", 116 Help: "time spent in database calls (ns)", 117 }, 118 methodLabels, 119 ), 120 size: prometheus.NewCounterVec( 121 prometheus.CounterOpts{ 122 Name: "size", 123 Help: "size of data passed in database calls", 124 }, 125 methodLabels, 126 ), 127 } 128 return meterDB, errors.Join( 129 reg.Register(meterDB.calls), 130 reg.Register(meterDB.duration), 131 reg.Register(meterDB.size), 132 ) 133 } 134 135 func (db *Database) Has(key []byte) (bool, error) { 136 start := time.Now() 137 has, err := db.db.Has(key) 138 duration := time.Since(start) 139 140 db.calls.With(hasLabel).Inc() 141 db.duration.With(hasLabel).Add(float64(duration)) 142 db.size.With(hasLabel).Add(float64(len(key))) 143 return has, err 144 } 145 146 func (db *Database) Get(key []byte) ([]byte, error) { 147 start := time.Now() 148 value, err := db.db.Get(key) 149 duration := time.Since(start) 150 151 db.calls.With(getLabel).Inc() 152 db.duration.With(getLabel).Add(float64(duration)) 153 db.size.With(getLabel).Add(float64(len(key) + len(value))) 154 return value, err 155 } 156 157 func (db *Database) Put(key, value []byte) error { 158 start := time.Now() 159 err := db.db.Put(key, value) 160 duration := time.Since(start) 161 162 db.calls.With(putLabel).Inc() 163 db.duration.With(putLabel).Add(float64(duration)) 164 db.size.With(putLabel).Add(float64(len(key) + len(value))) 165 return err 166 } 167 168 func (db *Database) Delete(key []byte) error { 169 start := time.Now() 170 err := db.db.Delete(key) 171 duration := time.Since(start) 172 173 db.calls.With(deleteLabel).Inc() 174 db.duration.With(deleteLabel).Add(float64(duration)) 175 db.size.With(deleteLabel).Add(float64(len(key))) 176 return err 177 } 178 179 func (db *Database) NewBatch() database.Batch { 180 start := time.Now() 181 b := &batch{ 182 batch: db.db.NewBatch(), 183 db: db, 184 } 185 duration := time.Since(start) 186 187 db.calls.With(newBatchLabel).Inc() 188 db.duration.With(newBatchLabel).Add(float64(duration)) 189 return b 190 } 191 192 func (db *Database) NewIterator() database.Iterator { 193 return db.NewIteratorWithStartAndPrefix(nil, nil) 194 } 195 196 func (db *Database) NewIteratorWithStart(start []byte) database.Iterator { 197 return db.NewIteratorWithStartAndPrefix(start, nil) 198 } 199 200 func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator { 201 return db.NewIteratorWithStartAndPrefix(nil, prefix) 202 } 203 204 func (db *Database) NewIteratorWithStartAndPrefix( 205 start, 206 prefix []byte, 207 ) database.Iterator { 208 startTime := time.Now() 209 it := &iterator{ 210 iterator: db.db.NewIteratorWithStartAndPrefix(start, prefix), 211 db: db, 212 } 213 duration := time.Since(startTime) 214 215 db.calls.With(newIteratorLabel).Inc() 216 db.duration.With(newIteratorLabel).Add(float64(duration)) 217 return it 218 } 219 220 func (db *Database) Compact(start, limit []byte) error { 221 startTime := time.Now() 222 err := db.db.Compact(start, limit) 223 duration := time.Since(startTime) 224 225 db.calls.With(compactLabel).Inc() 226 db.duration.With(compactLabel).Add(float64(duration)) 227 return err 228 } 229 230 func (db *Database) Close() error { 231 start := time.Now() 232 err := db.db.Close() 233 duration := time.Since(start) 234 235 db.calls.With(closeLabel).Inc() 236 db.duration.With(closeLabel).Add(float64(duration)) 237 return err 238 } 239 240 func (db *Database) HealthCheck(ctx context.Context) (interface{}, error) { 241 start := time.Now() 242 result, err := db.db.HealthCheck(ctx) 243 duration := time.Since(start) 244 245 db.calls.With(healthCheckLabel).Inc() 246 db.duration.With(healthCheckLabel).Add(float64(duration)) 247 return result, err 248 } 249 250 type batch struct { 251 batch database.Batch 252 db *Database 253 } 254 255 func (b *batch) Put(key, value []byte) error { 256 start := time.Now() 257 err := b.batch.Put(key, value) 258 duration := time.Since(start) 259 260 b.db.calls.With(batchPutLabel).Inc() 261 b.db.duration.With(batchPutLabel).Add(float64(duration)) 262 b.db.size.With(batchPutLabel).Add(float64(len(key) + len(value))) 263 return err 264 } 265 266 func (b *batch) Delete(key []byte) error { 267 start := time.Now() 268 err := b.batch.Delete(key) 269 duration := time.Since(start) 270 271 b.db.calls.With(batchDeleteLabel).Inc() 272 b.db.duration.With(batchDeleteLabel).Add(float64(duration)) 273 b.db.size.With(batchDeleteLabel).Add(float64(len(key))) 274 return err 275 } 276 277 func (b *batch) Size() int { 278 start := time.Now() 279 size := b.batch.Size() 280 duration := time.Since(start) 281 282 b.db.calls.With(batchSizeLabel).Inc() 283 b.db.duration.With(batchSizeLabel).Add(float64(duration)) 284 return size 285 } 286 287 func (b *batch) Write() error { 288 start := time.Now() 289 err := b.batch.Write() 290 duration := time.Since(start) 291 size := b.batch.Size() 292 293 b.db.calls.With(batchWriteLabel).Inc() 294 b.db.duration.With(batchWriteLabel).Add(float64(duration)) 295 b.db.size.With(batchWriteLabel).Add(float64(size)) 296 return err 297 } 298 299 func (b *batch) Reset() { 300 start := time.Now() 301 b.batch.Reset() 302 duration := time.Since(start) 303 304 b.db.calls.With(batchResetLabel).Inc() 305 b.db.duration.With(batchResetLabel).Add(float64(duration)) 306 } 307 308 func (b *batch) Replay(w database.KeyValueWriterDeleter) error { 309 start := time.Now() 310 err := b.batch.Replay(w) 311 duration := time.Since(start) 312 313 b.db.calls.With(batchReplayLabel).Inc() 314 b.db.duration.With(batchReplayLabel).Add(float64(duration)) 315 return err 316 } 317 318 func (b *batch) Inner() database.Batch { 319 start := time.Now() 320 inner := b.batch.Inner() 321 duration := time.Since(start) 322 323 b.db.calls.With(batchInnerLabel).Inc() 324 b.db.duration.With(batchInnerLabel).Add(float64(duration)) 325 return inner 326 } 327 328 type iterator struct { 329 iterator database.Iterator 330 db *Database 331 } 332 333 func (it *iterator) Next() bool { 334 start := time.Now() 335 next := it.iterator.Next() 336 duration := time.Since(start) 337 size := len(it.iterator.Key()) + len(it.iterator.Value()) 338 339 it.db.calls.With(iteratorNextLabel).Inc() 340 it.db.duration.With(iteratorNextLabel).Add(float64(duration)) 341 it.db.size.With(iteratorNextLabel).Add(float64(size)) 342 return next 343 } 344 345 func (it *iterator) Error() error { 346 start := time.Now() 347 err := it.iterator.Error() 348 duration := time.Since(start) 349 350 it.db.calls.With(iteratorErrorLabel).Inc() 351 it.db.duration.With(iteratorErrorLabel).Add(float64(duration)) 352 return err 353 } 354 355 func (it *iterator) Key() []byte { 356 start := time.Now() 357 key := it.iterator.Key() 358 duration := time.Since(start) 359 360 it.db.calls.With(iteratorKeyLabel).Inc() 361 it.db.duration.With(iteratorKeyLabel).Add(float64(duration)) 362 return key 363 } 364 365 func (it *iterator) Value() []byte { 366 start := time.Now() 367 value := it.iterator.Value() 368 duration := time.Since(start) 369 370 it.db.calls.With(iteratorValueLabel).Inc() 371 it.db.duration.With(iteratorValueLabel).Add(float64(duration)) 372 return value 373 } 374 375 func (it *iterator) Release() { 376 start := time.Now() 377 it.iterator.Release() 378 duration := time.Since(start) 379 380 it.db.calls.With(iteratorReleaseLabel).Inc() 381 it.db.duration.With(iteratorReleaseLabel).Add(float64(duration)) 382 }