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  }