github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/base/deletion_file.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package base 16 17 import ( 18 "os" 19 "sync" 20 "sync/atomic" 21 "time" 22 23 "github.com/zuoyebang/bitalosdb/internal/list2" 24 ) 25 26 type DFLOption struct { 27 IOWriteLoadThresholdCB func() bool 28 Logger Logger 29 DeleteInterval int 30 } 31 32 type DeletionFileLimiter struct { 33 opts *DFLOption 34 recvCh chan []string 35 closed atomic.Bool 36 exitCh chan struct{} 37 exitWg sync.WaitGroup 38 39 fileList struct { 40 sync.Mutex 41 l *list2.Queue 42 } 43 } 44 45 func NewDeletionFileLimiter(opts *DFLOption) *DeletionFileLimiter { 46 l := &DeletionFileLimiter{ 47 recvCh: make(chan []string, 1024), 48 exitCh: make(chan struct{}), 49 opts: opts, 50 } 51 l.fileList.l = list2.NewQueue() 52 l.closed.Store(false) 53 return l 54 } 55 56 func (d *DeletionFileLimiter) Run(opts *DFLOption) { 57 if opts != nil { 58 d.opts = opts 59 } 60 61 d.exitWg.Add(2) 62 63 go func() { 64 defer d.exitWg.Done() 65 66 d.opts.Logger.Info("[DELETELIMITER] produce running...") 67 68 for { 69 select { 70 case <-d.exitCh: 71 return 72 case files := <-d.recvCh: 73 if d.isClosed() { 74 return 75 } 76 77 d.pushFiles(files) 78 } 79 } 80 }() 81 82 go func() { 83 defer d.exitWg.Done() 84 85 duration := time.Duration(d.opts.DeleteInterval) * time.Second 86 d.opts.Logger.Infof("[DELETELIMITER] consume running interval:%s", duration) 87 t := time.NewTimer(duration) 88 defer t.Stop() 89 90 for { 91 select { 92 case <-d.exitCh: 93 return 94 case <-t.C: 95 if d.isClosed() { 96 return 97 } 98 99 if d.opts.IOWriteLoadThresholdCB() { 100 d.deleteFile() 101 } 102 103 t.Reset(duration) 104 } 105 } 106 }() 107 } 108 109 func (d *DeletionFileLimiter) AddFile(file string) { 110 if d.isClosed() { 111 return 112 } 113 114 d.recvCh <- []string{file} 115 } 116 117 func (d *DeletionFileLimiter) AddFiles(files []string) { 118 if d.isClosed() { 119 return 120 } 121 122 d.recvCh <- files 123 } 124 125 func (d *DeletionFileLimiter) Flush() { 126 if d.isClosed() { 127 return 128 } 129 130 for !d.fileList.l.Empty() { 131 d.deleteFile() 132 } 133 } 134 135 func (d *DeletionFileLimiter) Close() { 136 if d.isClosed() { 137 return 138 } 139 140 d.opts.Logger.Infof("[DELETELIMITER] closed start fileListLen:%d", d.fileList.l.Len()) 141 142 d.closed.Store(true) 143 close(d.exitCh) 144 d.exitWg.Wait() 145 146 for !d.fileList.l.Empty() { 147 d.deleteFile() 148 } 149 150 d.opts.Logger.Info("[DELETELIMITER] closed...") 151 } 152 153 func (d *DeletionFileLimiter) isClosed() bool { 154 return d.closed.Load() 155 } 156 157 func (d *DeletionFileLimiter) pushFiles(files []string) { 158 d.fileList.Lock() 159 defer d.fileList.Unlock() 160 161 for _, file := range files { 162 if len(file) == 0 { 163 continue 164 } 165 166 d.fileList.l.Push(file) 167 } 168 } 169 170 func (d *DeletionFileLimiter) popFile() (string, bool) { 171 d.fileList.Lock() 172 defer d.fileList.Unlock() 173 174 if d.fileList.l.Empty() { 175 return "", false 176 } 177 178 v := d.fileList.l.Pop() 179 if v == nil { 180 return "", true 181 } 182 if file, ok := v.(string); ok { 183 return file, true 184 } 185 return "", true 186 } 187 188 func (d *DeletionFileLimiter) deleteFile() { 189 path, ok := d.popFile() 190 if !ok || path == "" { 191 return 192 } 193 194 filename := GetFilePathBase(path) 195 if err := os.Remove(path); err != nil { 196 d.opts.Logger.Errorf("[DELETELIMITER] delete fail file:%s err:%s", filename, err.Error()) 197 } else { 198 d.opts.Logger.Infof("[DELETELIMITER] delete success file:%s", filename) 199 } 200 }