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

     1  package writer
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/Jeffail/benthos/v3/internal/bloblang/field"
     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  // CacheConfig contains configuration fields for the Cache output type.
    18  type CacheConfig struct {
    19  	Target      string `json:"target" yaml:"target"`
    20  	Key         string `json:"key" yaml:"key"`
    21  	TTL         string `json:"ttl" yaml:"ttl"`
    22  	MaxInFlight int    `json:"max_in_flight" yaml:"max_in_flight"`
    23  }
    24  
    25  // NewCacheConfig creates a new Config with default values.
    26  func NewCacheConfig() CacheConfig {
    27  	return CacheConfig{
    28  		Target:      "",
    29  		Key:         `${!count("items")}-${!timestamp_unix_nano()}`,
    30  		MaxInFlight: 1,
    31  	}
    32  }
    33  
    34  //------------------------------------------------------------------------------
    35  
    36  // Cache is a benthos writer.Type implementation that writes messages to a
    37  // Cache directory.
    38  type Cache struct {
    39  	conf CacheConfig
    40  	mgr  types.Manager
    41  
    42  	key *field.Expression
    43  	ttl *field.Expression
    44  
    45  	log   log.Modular
    46  	stats metrics.Type
    47  }
    48  
    49  // NewCache creates a new Cache writer.Type.
    50  func NewCache(
    51  	conf CacheConfig,
    52  	mgr types.Manager,
    53  	log log.Modular,
    54  	stats metrics.Type,
    55  ) (*Cache, error) {
    56  	key, err := interop.NewBloblangField(mgr, conf.Key)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("failed to parse key expression: %v", err)
    59  	}
    60  	ttl, err := interop.NewBloblangField(mgr, conf.TTL)
    61  	if err != nil {
    62  		return nil, fmt.Errorf("failed to parse ttl expression: %v", err)
    63  	}
    64  	if err := interop.ProbeCache(context.Background(), mgr, conf.Target); err != nil {
    65  		return nil, err
    66  	}
    67  	return &Cache{
    68  		conf:  conf,
    69  		mgr:   mgr,
    70  		key:   key,
    71  		ttl:   ttl,
    72  		log:   log,
    73  		stats: stats,
    74  	}, nil
    75  }
    76  
    77  // ConnectWithContext does nothing.
    78  func (c *Cache) ConnectWithContext(ctx context.Context) error {
    79  	return c.Connect()
    80  }
    81  
    82  // Connect does nothing.
    83  func (c *Cache) Connect() error {
    84  	c.log.Infof("Writing message parts as items in cache: %v\n", c.conf.Target)
    85  	return nil
    86  }
    87  
    88  func (c *Cache) writeMulti(ctx context.Context, msg types.Message) error {
    89  	var err error
    90  	if cerr := interop.AccessCache(context.Background(), c.mgr, c.conf.Target, func(cache types.Cache) {
    91  		if cttl, ok := cache.(types.CacheWithTTL); ok {
    92  			items := map[string]types.CacheTTLItem{}
    93  			if err = msg.Iter(func(i int, p types.Part) error {
    94  				var ttl *time.Duration
    95  				if ttls := c.ttl.String(i, msg); ttls != "" {
    96  					t, terr := time.ParseDuration(ttls)
    97  					if terr != nil {
    98  						c.log.Debugf("Invalid duration string for TTL field: %v\n", terr)
    99  						return fmt.Errorf("ttl field: %w", terr)
   100  					}
   101  					ttl = &t
   102  				}
   103  				items[c.key.String(i, msg)] = types.CacheTTLItem{
   104  					Value: p.Get(),
   105  					TTL:   ttl,
   106  				}
   107  				return nil
   108  			}); err != nil {
   109  				return
   110  			}
   111  			err = cttl.SetMultiWithTTL(items)
   112  			return
   113  		}
   114  		items := map[string][]byte{}
   115  		msg.Iter(func(i int, p types.Part) error {
   116  			items[c.key.String(i, msg)] = p.Get()
   117  			return nil
   118  		})
   119  		err = cache.SetMulti(items)
   120  	}); cerr != nil {
   121  		err = cerr
   122  	}
   123  	return err
   124  }
   125  
   126  // WriteWithContext attempts to write message contents to a target Cache.
   127  func (c *Cache) WriteWithContext(ctx context.Context, msg types.Message) error {
   128  	if msg.Len() > 1 {
   129  		return c.writeMulti(ctx, msg)
   130  	}
   131  	var err error
   132  	if cerr := interop.AccessCache(ctx, c.mgr, c.conf.Target, func(cache types.Cache) {
   133  		if cttl, ok := cache.(types.CacheWithTTL); ok {
   134  			var ttl *time.Duration
   135  			if ttls := c.ttl.String(0, msg); ttls != "" {
   136  				t, terr := time.ParseDuration(ttls)
   137  				if terr != nil {
   138  					c.log.Debugf("Invalid duration string for TTL field: %v\n", terr)
   139  					err = fmt.Errorf("ttl field: %w", terr)
   140  					return
   141  				}
   142  				ttl = &t
   143  			}
   144  			err = cttl.SetWithTTL(c.key.String(0, msg), msg.Get(0).Get(), ttl)
   145  			return
   146  		}
   147  		err = cache.Set(c.key.String(0, msg), msg.Get(0).Get())
   148  	}); cerr != nil {
   149  		err = cerr
   150  	}
   151  	return err
   152  }
   153  
   154  // Write attempts to write message contents to a target Cache.
   155  func (c *Cache) Write(msg types.Message) error {
   156  	return c.WriteWithContext(context.Background(), msg)
   157  }
   158  
   159  // CloseAsync begins cleaning up resources used by this writer asynchronously.
   160  func (c *Cache) CloseAsync() {
   161  }
   162  
   163  // WaitForClose will block until either the writer is closed or a specified
   164  // timeout occurs.
   165  func (c *Cache) WaitForClose(time.Duration) error {
   166  	return nil
   167  }
   168  
   169  //------------------------------------------------------------------------------