storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/event/target/queuestore.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  	"encoding/json"
    21  	"io/ioutil"
    22  	"math"
    23  	"os"
    24  	"path/filepath"
    25  	"sort"
    26  	"sync"
    27  
    28  	"storj.io/minio/pkg/event"
    29  	"storj.io/minio/pkg/sys"
    30  )
    31  
    32  const (
    33  	defaultLimit = 100000 // Default store limit.
    34  	eventExt     = ".event"
    35  )
    36  
    37  // QueueStore - Filestore for persisting events.
    38  type QueueStore struct {
    39  	sync.RWMutex
    40  	currentEntries uint64
    41  	entryLimit     uint64
    42  	directory      string
    43  }
    44  
    45  // NewQueueStore - Creates an instance for QueueStore.
    46  func NewQueueStore(directory string, limit uint64) Store {
    47  	if limit == 0 {
    48  		limit = defaultLimit
    49  		_, maxRLimit, err := sys.GetMaxOpenFileLimit()
    50  		if err == nil {
    51  			// Limit the maximum number of entries
    52  			// to maximum open file limit
    53  			if maxRLimit < limit {
    54  				limit = maxRLimit
    55  			}
    56  		}
    57  	}
    58  
    59  	return &QueueStore{
    60  		directory:  directory,
    61  		entryLimit: limit,
    62  	}
    63  }
    64  
    65  // Open - Creates the directory if not present.
    66  func (store *QueueStore) Open() error {
    67  	store.Lock()
    68  	defer store.Unlock()
    69  
    70  	if err := os.MkdirAll(store.directory, os.FileMode(0770)); err != nil {
    71  		return err
    72  	}
    73  
    74  	names, err := store.list()
    75  	if err != nil {
    76  		return err
    77  	}
    78  
    79  	currentEntries := uint64(len(names))
    80  	if currentEntries >= store.entryLimit {
    81  		return errLimitExceeded
    82  	}
    83  
    84  	store.currentEntries = currentEntries
    85  
    86  	return nil
    87  }
    88  
    89  // write - writes event to the directory.
    90  func (store *QueueStore) write(key string, e event.Event) error {
    91  
    92  	// Marshalls the event.
    93  	eventData, err := json.Marshal(e)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	path := filepath.Join(store.directory, key+eventExt)
    99  	if err := ioutil.WriteFile(path, eventData, os.FileMode(0770)); err != nil {
   100  		return err
   101  	}
   102  
   103  	// Increment the event count.
   104  	store.currentEntries++
   105  
   106  	return nil
   107  }
   108  
   109  // Put - puts a event to the store.
   110  func (store *QueueStore) Put(e event.Event) error {
   111  	store.Lock()
   112  	defer store.Unlock()
   113  	if store.currentEntries >= store.entryLimit {
   114  		return errLimitExceeded
   115  	}
   116  	key, err := getNewUUID()
   117  	if err != nil {
   118  		return err
   119  	}
   120  	return store.write(key, e)
   121  }
   122  
   123  // Get - gets a event from the store.
   124  func (store *QueueStore) Get(key string) (event event.Event, err error) {
   125  	store.RLock()
   126  
   127  	defer func(store *QueueStore) {
   128  		store.RUnlock()
   129  		if err != nil {
   130  			// Upon error we remove the entry.
   131  			store.Del(key)
   132  		}
   133  	}(store)
   134  
   135  	var eventData []byte
   136  	eventData, err = ioutil.ReadFile(filepath.Join(store.directory, key+eventExt))
   137  	if err != nil {
   138  		return event, err
   139  	}
   140  
   141  	if len(eventData) == 0 {
   142  		return event, os.ErrNotExist
   143  	}
   144  
   145  	if err = json.Unmarshal(eventData, &event); err != nil {
   146  		return event, err
   147  	}
   148  
   149  	return event, nil
   150  }
   151  
   152  // Del - Deletes an entry from the store.
   153  func (store *QueueStore) Del(key string) error {
   154  	store.Lock()
   155  	defer store.Unlock()
   156  	return store.del(key)
   157  }
   158  
   159  // lockless call
   160  func (store *QueueStore) del(key string) error {
   161  	if err := os.Remove(filepath.Join(store.directory, key+eventExt)); err != nil {
   162  		return err
   163  	}
   164  
   165  	// Decrement the current entries count.
   166  	store.currentEntries--
   167  
   168  	// Current entries can underflow, when multiple
   169  	// events are being pushed in parallel, this code
   170  	// is needed to ensure that we don't underflow.
   171  	//
   172  	// queueStore replayEvents is not serialized,
   173  	// this code is needed to protect us under
   174  	// such situations.
   175  	if store.currentEntries == math.MaxUint64 {
   176  		store.currentEntries = 0
   177  	}
   178  	return nil
   179  }
   180  
   181  // List - lists all files from the directory.
   182  func (store *QueueStore) List() ([]string, error) {
   183  	store.RLock()
   184  	defer store.RUnlock()
   185  	return store.list()
   186  }
   187  
   188  // list lock less.
   189  func (store *QueueStore) list() ([]string, error) {
   190  	var names []string
   191  	files, err := ioutil.ReadDir(store.directory)
   192  	if err != nil {
   193  		return names, err
   194  	}
   195  
   196  	// Sort the dentries.
   197  	sort.Slice(files, func(i, j int) bool {
   198  		return files[i].ModTime().Before(files[j].ModTime())
   199  	})
   200  
   201  	for _, file := range files {
   202  		names = append(names, file.Name())
   203  	}
   204  
   205  	return names, nil
   206  }