...

Source file src/github.com/LINBIT/golinstor/cache/cache.go

Documentation: github.com/LINBIT/golinstor/cache

     1  // Package cache
     2  //
     3  // Implement client side caching for client.Client. This is useful for burst-happy applications that will try to query
     4  // a lot of the same information in small chunks.
     5  //
     6  // For example, an application could try to check the state of nodes, but do so using one request per node. This is
     7  // obviously not ideal in larger cluster, where it would be more efficient to request the state of all nodes at once.
     8  // Depending on the application, this may not be possible, however.
     9  //
    10  // This package contains ready-to-use client side caches with configurable duration and automatic invalidation under the
    11  // assumption that modifications are made from the same client.
    12  package cache
    13  
    14  import (
    15  	"sync"
    16  	"time"
    17  
    18  	"github.com/LINBIT/golinstor/client"
    19  )
    20  
    21  var (
    22  	yes      = true
    23  	cacheOpt = &client.ListOpts{Cached: &yes}
    24  )
    25  
    26  type Cache interface {
    27  	apply(c *client.Client)
    28  }
    29  
    30  // WithCaches sets up the given caches on the client.Client.
    31  func WithCaches(caches ...Cache) client.Option {
    32  	return func(cl *client.Client) error {
    33  		for _, ca := range caches {
    34  			ca.apply(cl)
    35  		}
    36  
    37  		return nil
    38  	}
    39  }
    40  
    41  type cache struct {
    42  	mu         sync.Mutex
    43  	lastUpdate time.Time
    44  	cache      any
    45  }
    46  
    47  // Invalidate forcefully resets the cache.
    48  // The next call to Get will always invoke the provided function.
    49  func (c *cache) Invalidate() {
    50  	c.mu.Lock()
    51  	c.lastUpdate = time.Time{}
    52  	c.cache = nil
    53  	c.mu.Unlock()
    54  }
    55  
    56  // Get returns a cached response or the result of the provided update function.
    57  //
    58  // If the cache is current, it will return the last successful cached response.
    59  // If the cache is outdated, it will run the provided function to retrieve a result. A successful response
    60  // is cached for later use.
    61  func (c *cache) Get(timeout time.Duration, updateFunc func() (any, error)) (any, error) {
    62  	c.mu.Lock()
    63  	defer c.mu.Unlock()
    64  
    65  	now := time.Now()
    66  
    67  	if timeout != 0 && c.lastUpdate.Add(timeout).Before(now) {
    68  		result, err := updateFunc()
    69  		if err != nil {
    70  			return nil, err
    71  		}
    72  
    73  		c.cache = result
    74  		c.lastUpdate = now
    75  	}
    76  
    77  	return c.cache, nil
    78  }
    79  
    80  // filterNodeAndPoolOpts filters generic items based on the provided client.ListOpts
    81  // This tries to mimic the behaviour of LINSTOR when using the node and storage pool query parameters.
    82  func filterNodeAndPoolOpts[T any](items []T, getNodeAndPoolNames func(*T) ([]string, []string), opts ...*client.ListOpts) []T {
    83  	filterNames := make(map[string]struct{})
    84  	filterPools := make(map[string]struct{})
    85  
    86  	for _, o := range opts {
    87  		for _, n := range o.Node {
    88  			filterNames[n] = struct{}{}
    89  		}
    90  
    91  		for _, sp := range o.StoragePool {
    92  			filterPools[sp] = struct{}{}
    93  		}
    94  	}
    95  
    96  	var result []T
    97  
    98  outer:
    99  	for i := range items {
   100  		nodes, pools := getNodeAndPoolNames(&items[i])
   101  
   102  		if len(filterNames) > 0 {
   103  			for _, nodeName := range nodes {
   104  				if _, ok := filterNames[nodeName]; !ok {
   105  					continue outer
   106  				}
   107  			}
   108  		}
   109  
   110  		if len(filterPools) > 0 {
   111  			for _, poolName := range pools {
   112  				if _, ok := filterPools[poolName]; !ok {
   113  					continue outer
   114  				}
   115  			}
   116  		}
   117  
   118  		result = append(result, items[i])
   119  	}
   120  
   121  	return result
   122  }
   123  

View as plain text