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 //------------------------------------------------------------------------------