github.com/Jeffail/benthos/v3@v3.65.0/lib/cache/multilevel.go (about)

     1  package cache
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/Jeffail/benthos/v3/internal/docs"
     9  	"github.com/Jeffail/benthos/v3/internal/interop"
    10  	"github.com/Jeffail/benthos/v3/lib/log"
    11  	"github.com/Jeffail/benthos/v3/lib/metrics"
    12  	"github.com/Jeffail/benthos/v3/lib/types"
    13  )
    14  
    15  //------------------------------------------------------------------------------
    16  
    17  func init() {
    18  	Constructors[TypeMultilevel] = TypeSpec{
    19  		constructor: NewMultilevel,
    20  		Summary: `
    21  Combines multiple caches as levels, performing read-through and write-through
    22  operations across them.`,
    23  		Footnotes: `
    24  For the Add command this cache first checks all levels except the last for the
    25  key. If the key is not found it is added to the final cache level, if that
    26  succeeds all higher cache levels have the key set.
    27  
    28  ## Examples
    29  
    30  It's possible to use multilevel to create a warm cache in memory above a cold
    31  remote cache:
    32  
    33  ` + "```yaml" + `
    34  pipeline:
    35    processors:
    36      - branch:
    37          processors:
    38            - cache:
    39                resource: leveled
    40                operator: get
    41                key: ${! json("key") }
    42            - catch:
    43              - bloblang: 'root = {"err":error()}'
    44          result_map: 'root.result = this'
    45  
    46  cache_resources:
    47    - label: leveled
    48      multilevel: [ hot, cold ]
    49  
    50    - label: hot
    51      memory:
    52        ttl: 300
    53  
    54    - label: cold
    55      memcached:
    56        addresses: [ TODO:11211 ]
    57        ttl: 3600
    58  ` + "```" + `
    59  
    60  Using this config when a target key already exists in our local memory cache we
    61  won't bother hitting the remote memcached instance.`,
    62  		config: docs.FieldComponent().Array().HasType(docs.FieldTypeString).HasDefault([]string{}),
    63  	}
    64  }
    65  
    66  //------------------------------------------------------------------------------
    67  
    68  // MultilevelConfig contains config fields for the Multilevel cache type.
    69  type MultilevelConfig []string
    70  
    71  // NewMultilevelConfig creates a MultilevelConfig populated with default values.
    72  func NewMultilevelConfig() MultilevelConfig {
    73  	return []string{}
    74  }
    75  
    76  //------------------------------------------------------------------------------
    77  
    78  // Multilevel is a file system based cache implementation.
    79  type Multilevel struct {
    80  	mgr    types.Manager
    81  	log    log.Modular
    82  	caches []string
    83  }
    84  
    85  // NewMultilevel creates a new Multilevel cache type.
    86  func NewMultilevel(conf Config, mgr types.Manager, log log.Modular, stats metrics.Type) (types.Cache, error) {
    87  	if len(conf.Multilevel) < 2 {
    88  		return nil, fmt.Errorf("expected at least two cache levels, found %v", len(conf.Multilevel))
    89  	}
    90  	for _, name := range conf.Multilevel {
    91  		if err := interop.ProbeCache(context.Background(), mgr, name); err != nil {
    92  			return nil, err
    93  		}
    94  	}
    95  	return &Multilevel{
    96  		mgr:    mgr,
    97  		log:    log,
    98  		caches: conf.Multilevel,
    99  	}, nil
   100  }
   101  
   102  //------------------------------------------------------------------------------
   103  
   104  func (l *Multilevel) setUpToLevelPassive(i int, key string, value []byte) {
   105  	for j, name := range l.caches {
   106  		if j == i {
   107  			break
   108  		}
   109  		var setErr error
   110  		err := interop.AccessCache(context.Background(), l.mgr, name, func(c types.Cache) {
   111  			setErr = c.Set(key, value)
   112  		})
   113  		if err != nil {
   114  			l.log.Errorf("Unable to passively set key '%v' for cache '%v': %v\n", key, name, err)
   115  		}
   116  		if setErr != nil {
   117  			l.log.Errorf("Unable to passively set key '%v' for cache '%v': %v\n", key, name, setErr)
   118  		}
   119  	}
   120  }
   121  
   122  // Get attempts to locate and return a cached value by its key, returns an error
   123  // if the key does not exist.
   124  func (l *Multilevel) Get(key string) ([]byte, error) {
   125  	for i, name := range l.caches {
   126  		var data []byte
   127  		var err error
   128  		if cerr := interop.AccessCache(context.Background(), l.mgr, name, func(c types.Cache) {
   129  			data, err = c.Get(key)
   130  		}); cerr != nil {
   131  			return nil, fmt.Errorf("unable to access cache '%v': %v", name, cerr)
   132  		}
   133  		if err != nil {
   134  			if err != types.ErrKeyNotFound {
   135  				return nil, err
   136  			}
   137  		} else {
   138  			l.setUpToLevelPassive(i, key, data)
   139  			return data, nil
   140  		}
   141  	}
   142  	return nil, types.ErrKeyNotFound
   143  }
   144  
   145  // SetWithTTL attempts to set the value of a key.
   146  func (l *Multilevel) SetWithTTL(key string, value []byte, ttl *time.Duration) error {
   147  	for _, name := range l.caches {
   148  		var err error
   149  		if cerr := interop.AccessCache(context.Background(), l.mgr, name, func(c types.Cache) {
   150  			if cttl, ok := c.(types.CacheWithTTL); ok {
   151  				err = cttl.SetWithTTL(key, value, ttl)
   152  			} else {
   153  				err = c.Set(key, value)
   154  			}
   155  		}); cerr != nil {
   156  			return fmt.Errorf("unable to access cache '%v': %v", name, cerr)
   157  		}
   158  		if err != nil {
   159  			return err
   160  		}
   161  	}
   162  	return nil
   163  }
   164  
   165  // Set attempts to set the value of a key.
   166  func (l *Multilevel) Set(key string, value []byte) error {
   167  	return l.SetWithTTL(key, value, nil)
   168  }
   169  
   170  // SetMultiWithTTL attempts to set the value of multiple keys, returns an error if any
   171  // keys fail.
   172  func (l *Multilevel) SetMultiWithTTL(items map[string]types.CacheTTLItem) error {
   173  	for _, name := range l.caches {
   174  		var err error
   175  		if cerr := interop.AccessCache(context.Background(), l.mgr, name, func(c types.Cache) {
   176  			if cttl, ok := c.(types.CacheWithTTL); ok {
   177  				err = cttl.SetMultiWithTTL(items)
   178  			} else {
   179  				sitems := make(map[string][]byte, len(items))
   180  				for k, v := range items {
   181  					sitems[k] = v.Value
   182  				}
   183  				err = c.SetMulti(sitems)
   184  			}
   185  		}); cerr != nil {
   186  			return fmt.Errorf("unable to access cache '%v': %v", name, cerr)
   187  		}
   188  		if err != nil {
   189  			return err
   190  		}
   191  	}
   192  	return nil
   193  }
   194  
   195  // SetMulti attempts to set the value of multiple keys, returns an error if any
   196  // keys fail.
   197  func (l *Multilevel) SetMulti(items map[string][]byte) error {
   198  	sitems := make(map[string]types.CacheTTLItem, len(items))
   199  	for k, v := range items {
   200  		sitems[k] = types.CacheTTLItem{
   201  			Value: v,
   202  		}
   203  	}
   204  	return l.SetMultiWithTTL(sitems)
   205  }
   206  
   207  // AddWithTTL attempts to set the value of a key only if the key does not already exist
   208  // and returns an error if the key already exists.
   209  func (l *Multilevel) AddWithTTL(key string, value []byte, ttl *time.Duration) error {
   210  	for i := 0; i < len(l.caches)-1; i++ {
   211  		var err error
   212  		if cerr := interop.AccessCache(context.Background(), l.mgr, l.caches[i], func(c types.Cache) {
   213  			_, err = c.Get(key)
   214  		}); cerr != nil {
   215  			return fmt.Errorf("unable to access cache '%v': %v", l.caches[i], cerr)
   216  		}
   217  		if err != nil {
   218  			if err != types.ErrKeyNotFound {
   219  				return err
   220  			}
   221  		} else {
   222  			return types.ErrKeyAlreadyExists
   223  		}
   224  	}
   225  
   226  	var err error
   227  	if cerr := interop.AccessCache(context.Background(), l.mgr, l.caches[len(l.caches)-1], func(c types.Cache) {
   228  		if cttl, ok := c.(types.CacheWithTTL); ok {
   229  			err = cttl.AddWithTTL(key, value, ttl)
   230  		} else {
   231  			err = c.Add(key, value)
   232  		}
   233  	}); cerr != nil {
   234  		return fmt.Errorf("unable to access cache '%v': %v", l.caches[len(l.caches)-1], cerr)
   235  	}
   236  	if err != nil {
   237  		return err
   238  	}
   239  
   240  	for i := len(l.caches) - 2; i >= 0; i-- {
   241  		if cerr := interop.AccessCache(context.Background(), l.mgr, l.caches[i], func(c types.Cache) {
   242  			if cttl, ok := c.(types.CacheWithTTL); ok {
   243  				err = cttl.AddWithTTL(key, value, ttl)
   244  			} else {
   245  				err = c.Add(key, value)
   246  			}
   247  		}); cerr != nil {
   248  			return fmt.Errorf("unable to access cache '%v': %v", l.caches[i], cerr)
   249  		}
   250  		if err != nil {
   251  			return err
   252  		}
   253  	}
   254  	return nil
   255  }
   256  
   257  // Add attempts to set the value of a key only if the key does not already exist
   258  // and returns an error if the key already exists.
   259  func (l *Multilevel) Add(key string, value []byte) error {
   260  	return l.AddWithTTL(key, value, nil)
   261  }
   262  
   263  // Delete attempts to remove a key.
   264  func (l *Multilevel) Delete(key string) error {
   265  	for _, name := range l.caches {
   266  		var err error
   267  		if cerr := interop.AccessCache(context.Background(), l.mgr, name, func(c types.Cache) {
   268  			err = c.Delete(key)
   269  		}); cerr != nil {
   270  			return fmt.Errorf("unable to access cache '%v': %v", name, cerr)
   271  		}
   272  		if err != nil && err != types.ErrKeyNotFound {
   273  			return err
   274  		}
   275  	}
   276  	return nil
   277  }
   278  
   279  // CloseAsync shuts down the cache.
   280  func (l *Multilevel) CloseAsync() {
   281  }
   282  
   283  // WaitForClose blocks until the cache has closed down.
   284  func (l *Multilevel) WaitForClose(timeout time.Duration) error {
   285  	return nil
   286  }
   287  
   288  //------------------------------------------------------------------------------