github.com/Jeffail/benthos/v3@v3.65.0/lib/input/reader/redis_list.go (about)

     1  package reader
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	bredis "github.com/Jeffail/benthos/v3/internal/impl/redis"
    10  	"github.com/Jeffail/benthos/v3/lib/log"
    11  	"github.com/Jeffail/benthos/v3/lib/message"
    12  	"github.com/Jeffail/benthos/v3/lib/metrics"
    13  	"github.com/Jeffail/benthos/v3/lib/types"
    14  	"github.com/go-redis/redis/v7"
    15  )
    16  
    17  //------------------------------------------------------------------------------
    18  
    19  // RedisListConfig contains configuration fields for the RedisList input type.
    20  type RedisListConfig struct {
    21  	bredis.Config `json:",inline" yaml:",inline"`
    22  	Key           string `json:"key" yaml:"key"`
    23  	Timeout       string `json:"timeout" yaml:"timeout"`
    24  }
    25  
    26  // NewRedisListConfig creates a new RedisListConfig with default values.
    27  func NewRedisListConfig() RedisListConfig {
    28  	return RedisListConfig{
    29  		Config:  bredis.NewConfig(),
    30  		Key:     "benthos_list",
    31  		Timeout: "5s",
    32  	}
    33  }
    34  
    35  //------------------------------------------------------------------------------
    36  
    37  // RedisList is an input type that reads Redis List messages.
    38  type RedisList struct {
    39  	client redis.UniversalClient
    40  	cMut   sync.Mutex
    41  
    42  	conf    RedisListConfig
    43  	timeout time.Duration
    44  
    45  	stats metrics.Type
    46  	log   log.Modular
    47  }
    48  
    49  // NewRedisList creates a new RedisList input type.
    50  func NewRedisList(
    51  	conf RedisListConfig, log log.Modular, stats metrics.Type,
    52  ) (*RedisList, error) {
    53  	r := &RedisList{
    54  		conf:  conf,
    55  		stats: stats,
    56  		log:   log,
    57  	}
    58  
    59  	if tout := conf.Timeout; len(tout) > 0 {
    60  		var err error
    61  		if r.timeout, err = time.ParseDuration(tout); err != nil {
    62  			return nil, fmt.Errorf("failed to parse timeout string: %v", err)
    63  		}
    64  	}
    65  
    66  	if _, err := conf.Config.Client(); err != nil {
    67  		return nil, err
    68  	}
    69  	return r, nil
    70  }
    71  
    72  //------------------------------------------------------------------------------
    73  
    74  // Connect establishes a connection to a Redis server.
    75  func (r *RedisList) Connect() error {
    76  	return r.ConnectWithContext(context.Background())
    77  }
    78  
    79  // ConnectWithContext establishes a connection to a Redis server.
    80  func (r *RedisList) ConnectWithContext(ctx context.Context) error {
    81  	r.cMut.Lock()
    82  	defer r.cMut.Unlock()
    83  
    84  	if r.client != nil {
    85  		return nil
    86  	}
    87  
    88  	client, err := r.conf.Config.Client()
    89  	if err == nil {
    90  		_, err = client.Ping().Result()
    91  	}
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	r.log.Infof("Receiving messages from Redis list: %v\n", r.conf.Key)
    97  
    98  	r.client = client
    99  	return nil
   100  }
   101  
   102  // Read attempts to pop a message from a Redis list.
   103  func (r *RedisList) Read() (types.Message, error) {
   104  	msg, _, err := r.ReadWithContext(context.Background())
   105  	return msg, err
   106  }
   107  
   108  // ReadWithContext attempts to pop a message from a Redis list.
   109  func (r *RedisList) ReadWithContext(ctx context.Context) (types.Message, AsyncAckFn, error) {
   110  	var client redis.UniversalClient
   111  
   112  	r.cMut.Lock()
   113  	client = r.client
   114  	r.cMut.Unlock()
   115  
   116  	if client == nil {
   117  		return nil, nil, types.ErrNotConnected
   118  	}
   119  
   120  	res, err := client.BLPop(r.timeout, r.conf.Key).Result()
   121  
   122  	if err != nil && err != redis.Nil {
   123  		r.disconnect()
   124  		r.log.Errorf("Error from redis: %v\n", err)
   125  		return nil, nil, types.ErrNotConnected
   126  	}
   127  
   128  	if len(res) < 2 {
   129  		return nil, nil, types.ErrTimeout
   130  	}
   131  
   132  	return message.New([][]byte{[]byte(res[1])}), noopAsyncAckFn, nil
   133  }
   134  
   135  // Acknowledge is a noop since Redis Lists do not support acknowledgements.
   136  func (r *RedisList) Acknowledge(err error) error {
   137  	return nil
   138  }
   139  
   140  // disconnect safely closes a connection to an RedisList server.
   141  func (r *RedisList) disconnect() error {
   142  	r.cMut.Lock()
   143  	defer r.cMut.Unlock()
   144  
   145  	var err error
   146  	if r.client != nil {
   147  		err = r.client.Close()
   148  		r.client = nil
   149  	}
   150  	return err
   151  }
   152  
   153  // CloseAsync shuts down the RedisList input and stops processing requests.
   154  func (r *RedisList) CloseAsync() {
   155  	r.disconnect()
   156  }
   157  
   158  // WaitForClose blocks until the RedisList input has closed down.
   159  func (r *RedisList) WaitForClose(timeout time.Duration) error {
   160  	return nil
   161  }
   162  
   163  //------------------------------------------------------------------------------