github.com/ethersphere/bee/v2@v2.2.0/pkg/storer/cachestore.go (about)

     1  // Copyright 2023 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package storer
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"time"
    12  
    13  	storage "github.com/ethersphere/bee/v2/pkg/storage"
    14  	"github.com/ethersphere/bee/v2/pkg/storer/internal/transaction"
    15  	"github.com/ethersphere/bee/v2/pkg/swarm"
    16  )
    17  
    18  const (
    19  	cacheOverCapacity = "cacheOverCapacity"
    20  )
    21  
    22  func (db *DB) cacheWorker(ctx context.Context) {
    23  
    24  	defer db.inFlight.Done()
    25  
    26  	overCapTrigger, overCapUnsub := db.events.Subscribe(cacheOverCapacity)
    27  	defer overCapUnsub()
    28  
    29  	db.triggerCacheEviction()
    30  
    31  	for {
    32  		select {
    33  		case <-ctx.Done():
    34  			return
    35  		case <-overCapTrigger:
    36  
    37  			size, capc := db.cacheObj.Size(), db.cacheObj.Capacity()
    38  			if size <= capc {
    39  				continue
    40  			}
    41  
    42  			evict := size - capc
    43  			if evict < db.opts.cacheMinEvictCount { // evict at least a min count
    44  				evict = db.opts.cacheMinEvictCount
    45  			}
    46  
    47  			dur := captureDuration(time.Now())
    48  			err := db.cacheObj.RemoveOldest(ctx, db.storage, evict)
    49  			db.metrics.MethodCallsDuration.WithLabelValues("cachestore", "RemoveOldest").Observe(dur())
    50  			if err != nil {
    51  				db.metrics.MethodCalls.WithLabelValues("cachestore", "RemoveOldest", "failure").Inc()
    52  				db.logger.Warning("cache eviction failure", "error", err)
    53  			} else {
    54  				db.logger.Debug("cache eviction finished", "evicted", evict, "duration_sec", dur())
    55  				db.metrics.MethodCalls.WithLabelValues("cachestore", "RemoveOldest", "success").Inc()
    56  			}
    57  			db.triggerCacheEviction()
    58  		case <-db.quit:
    59  			return
    60  		}
    61  	}
    62  }
    63  
    64  // Lookup is the implementation of the CacheStore.Lookup method.
    65  func (db *DB) Lookup() storage.Getter {
    66  	return getterWithMetrics{
    67  		storage.GetterFunc(func(ctx context.Context, address swarm.Address) (swarm.Chunk, error) {
    68  			ch, err := db.cacheObj.Getter(db.storage).Get(ctx, address)
    69  			switch {
    70  			case err == nil:
    71  				return ch, nil
    72  			case errors.Is(err, storage.ErrNotFound):
    73  				// here we would ideally have nothing to do but just to return this
    74  				// error to the client. The commit is mainly done to end the txn.
    75  				return nil, err
    76  			}
    77  			// if we are here, it means there was some unexpected error, in which
    78  			// case we need to rollback any changes that were already made.
    79  			return nil, fmt.Errorf("cache.Get: %w", err)
    80  		}),
    81  		db.metrics,
    82  		"cachestore",
    83  	}
    84  }
    85  
    86  // Cache is the implementation of the CacheStore.Cache method.
    87  func (db *DB) Cache() storage.Putter {
    88  	return putterWithMetrics{
    89  		storage.PutterFunc(func(ctx context.Context, ch swarm.Chunk) error {
    90  			defer db.triggerCacheEviction()
    91  			err := db.cacheObj.Putter(db.storage).Put(ctx, ch)
    92  			if err != nil {
    93  				return fmt.Errorf("cache.Put: %w", err)
    94  			}
    95  			return nil
    96  		}),
    97  		db.metrics,
    98  		"cachestore",
    99  	}
   100  }
   101  
   102  // CacheShallowCopy creates cache entries with the expectation that the chunk already exists in the chunkstore.
   103  func (db *DB) CacheShallowCopy(ctx context.Context, store transaction.Storage, addrs ...swarm.Address) error {
   104  	defer db.triggerCacheEviction()
   105  	dur := captureDuration(time.Now())
   106  	err := db.cacheObj.ShallowCopy(ctx, store, addrs...)
   107  	db.metrics.MethodCallsDuration.WithLabelValues("cachestore", "ShallowCopy").Observe(dur())
   108  	if err != nil {
   109  		err = fmt.Errorf("cache shallow copy: %w", err)
   110  		db.metrics.MethodCalls.WithLabelValues("cachestore", "ShallowCopy", "failure").Inc()
   111  	} else {
   112  		db.metrics.MethodCalls.WithLabelValues("cachestore", "ShallowCopy", "success").Inc()
   113  	}
   114  	return err
   115  }
   116  
   117  func (db *DB) triggerCacheEviction() {
   118  
   119  	var (
   120  		size = db.cacheObj.Size()
   121  		capc = db.cacheObj.Capacity()
   122  	)
   123  	db.metrics.CacheSize.Set(float64(size))
   124  
   125  	if size > capc {
   126  		db.events.Trigger(cacheOverCapacity)
   127  	}
   128  }