storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/event/target/store.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2019 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package target
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"strings"
    24  	"syscall"
    25  	"time"
    26  
    27  	"storj.io/minio/pkg/event"
    28  )
    29  
    30  const retryInterval = 3 * time.Second
    31  
    32  // errNotConnected - indicates that the target connection is not active.
    33  var errNotConnected = errors.New("not connected to target server/service")
    34  
    35  // errLimitExceeded error is sent when the maximum limit is reached.
    36  var errLimitExceeded = errors.New("the maximum store limit reached")
    37  
    38  // Store - To persist the events.
    39  type Store interface {
    40  	Put(event event.Event) error
    41  	Get(key string) (event.Event, error)
    42  	List() ([]string, error)
    43  	Del(key string) error
    44  	Open() error
    45  }
    46  
    47  // replayEvents - Reads the events from the store and replays.
    48  func replayEvents(store Store, doneCh <-chan struct{}, loggerOnce func(ctx context.Context, err error, id interface{}, kind ...interface{}), id event.TargetID) <-chan string {
    49  	eventKeyCh := make(chan string)
    50  
    51  	go func() {
    52  		retryTicker := time.NewTicker(retryInterval)
    53  		defer retryTicker.Stop()
    54  		defer close(eventKeyCh)
    55  		for {
    56  			names, err := store.List()
    57  			if err == nil {
    58  				for _, name := range names {
    59  					select {
    60  					case eventKeyCh <- strings.TrimSuffix(name, eventExt):
    61  						// Get next key.
    62  					case <-doneCh:
    63  						return
    64  					}
    65  				}
    66  			}
    67  
    68  			if len(names) < 2 {
    69  				select {
    70  				case <-retryTicker.C:
    71  					if err != nil {
    72  						loggerOnce(context.Background(),
    73  							fmt.Errorf("store.List() failed '%w'", err), id)
    74  					}
    75  				case <-doneCh:
    76  					return
    77  				}
    78  			}
    79  		}
    80  	}()
    81  
    82  	return eventKeyCh
    83  }
    84  
    85  // IsConnRefusedErr - To check fot "connection refused" error.
    86  func IsConnRefusedErr(err error) bool {
    87  	return errors.Is(err, syscall.ECONNREFUSED)
    88  }
    89  
    90  // IsConnResetErr - Checks for connection reset errors.
    91  func IsConnResetErr(err error) bool {
    92  	if strings.Contains(err.Error(), "connection reset by peer") {
    93  		return true
    94  	}
    95  	// incase if error message is wrapped.
    96  	return errors.Is(err, syscall.ECONNRESET)
    97  }
    98  
    99  // sendEvents - Reads events from the store and re-plays.
   100  func sendEvents(target event.Target, eventKeyCh <-chan string, doneCh <-chan struct{}, loggerOnce func(ctx context.Context, err error, id interface{}, kind ...interface{})) {
   101  	retryTicker := time.NewTicker(retryInterval)
   102  	defer retryTicker.Stop()
   103  
   104  	send := func(eventKey string) bool {
   105  		for {
   106  			err := target.Send(eventKey)
   107  			if err == nil {
   108  				break
   109  			}
   110  
   111  			if err != errNotConnected && !IsConnResetErr(err) {
   112  				loggerOnce(context.Background(),
   113  					fmt.Errorf("target.Send() failed with '%w'", err),
   114  					target.ID())
   115  			}
   116  
   117  			// Retrying after 3secs back-off
   118  
   119  			select {
   120  			case <-retryTicker.C:
   121  			case <-doneCh:
   122  				return false
   123  			}
   124  		}
   125  		return true
   126  	}
   127  
   128  	for {
   129  		select {
   130  		case eventKey, ok := <-eventKeyCh:
   131  			if !ok {
   132  				// closed channel.
   133  				return
   134  			}
   135  
   136  			if !send(eventKey) {
   137  				return
   138  			}
   139  		case <-doneCh:
   140  			return
   141  		}
   142  	}
   143  }