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 }