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  }