github.com/GuanceCloud/cliutils@v1.1.21/diskcache/open.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package diskcache
     7  
     8  import (
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"sort"
    13  	"sync"
    14  	"time"
    15  )
    16  
    17  // Open init and create a new disk cache. We can set other options with various options.
    18  func Open(opts ...CacheOption) (*DiskCache, error) {
    19  	c := defaultInstance()
    20  
    21  	// apply extra options
    22  	for _, x := range opts {
    23  		if x != nil {
    24  			x(c)
    25  		}
    26  	}
    27  
    28  	if err := c.doOpen(); err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	defer func() {
    33  		c.labels = append(c.labels,
    34  			fmt.Sprintf("%v", c.noFallbackOnError),
    35  			fmt.Sprintf("%v", c.noLock),
    36  			fmt.Sprintf("%v", c.noPos),
    37  			fmt.Sprintf("%v", c.noSync),
    38  			c.path,
    39  		)
    40  
    41  		openTimeVec.WithLabelValues(c.labels...).Set(float64(time.Now().Unix()))
    42  	}()
    43  
    44  	return c, nil
    45  }
    46  
    47  func defaultInstance() *DiskCache {
    48  	return &DiskCache{
    49  		noSync: false,
    50  
    51  		batchSize:   20 * 1024 * 1024,
    52  		maxDataSize: 0, // not set
    53  
    54  		wlock:  &sync.Mutex{},
    55  		rlock:  &sync.Mutex{},
    56  		rwlock: &sync.Mutex{},
    57  
    58  		wakeup:    time.Second * 3,
    59  		dirPerms:  0o750,
    60  		filePerms: 0o640,
    61  		pos: &pos{
    62  			Seek: 0,
    63  			Name: nil,
    64  		},
    65  	}
    66  }
    67  
    68  func (c *DiskCache) doOpen() error {
    69  	if c.dirPerms == 0 {
    70  		c.dirPerms = 0o755
    71  	}
    72  
    73  	if c.filePerms == 0 {
    74  		c.filePerms = 0o640
    75  	}
    76  
    77  	if c.batchSize == 0 {
    78  		c.batchSize = 20 * 1024 * 1024
    79  	}
    80  
    81  	if int64(c.maxDataSize) > c.batchSize {
    82  		// reset max-data-size to half of batch size
    83  		c.maxDataSize = int32(c.batchSize / 2)
    84  	}
    85  
    86  	if err := os.MkdirAll(c.path, c.dirPerms); err != nil {
    87  		return err
    88  	}
    89  
    90  	// disable open multiple times
    91  	if !c.noLock {
    92  		fl := newFlock(c.path)
    93  		if err := fl.lock(); err != nil {
    94  			return fmt.Errorf("lock: %w", err)
    95  		} else {
    96  			c.flock = fl
    97  		}
    98  	}
    99  
   100  	if !c.noPos {
   101  		// use `.pos' file to remember the reading position.
   102  		c.pos.fname = filepath.Join(c.path, ".pos")
   103  	}
   104  	c.curWriteFile = filepath.Join(c.path, "data")
   105  
   106  	c.syncEnv()
   107  
   108  	// set stable metrics
   109  	capVec.WithLabelValues(c.path).Set(float64(c.capacity))
   110  	maxDataVec.WithLabelValues(c.path).Set(float64(c.maxDataSize))
   111  	batchSizeVec.WithLabelValues(c.path).Set(float64(c.batchSize))
   112  
   113  	// write append fd, always write to the same-name file
   114  	if err := c.openWriteFile(); err != nil {
   115  		return err
   116  	}
   117  
   118  	// list files under @path
   119  	if err := filepath.Walk(c.path,
   120  		func(path string, fi os.FileInfo, err error) error {
   121  			if err != nil {
   122  				return err
   123  			}
   124  
   125  			if fi.IsDir() {
   126  				return nil
   127  			}
   128  
   129  			switch filepath.Base(path) {
   130  			case ".lock", ".pos": // ignore them
   131  			case "data": // count on size
   132  				c.size += fi.Size()
   133  			default:
   134  				c.size += fi.Size()
   135  				c.dataFiles = append(c.dataFiles, path)
   136  			}
   137  
   138  			return nil
   139  		}); err != nil {
   140  		return err
   141  	}
   142  
   143  	sort.Strings(c.dataFiles) // make file-name sorted for FIFO Get()
   144  
   145  	// first get, try load .pos
   146  	if !c.noPos {
   147  		if err := c.loadUnfinishedFile(); err != nil {
   148  			return err
   149  		}
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  // Close reclame fd resources.
   156  // Close is safe to call concurrently with other operations and will
   157  // block until all other operations finish.
   158  func (c *DiskCache) Close() error {
   159  	c.rwlock.Lock()
   160  	defer c.rwlock.Unlock()
   161  
   162  	defer func() {
   163  		lastCloseTimeVec.WithLabelValues(c.path).Set(float64(time.Now().Unix()))
   164  	}()
   165  
   166  	if c.rfd != nil {
   167  		if err := c.rfd.Close(); err != nil {
   168  			return err
   169  		}
   170  		c.rfd = nil
   171  	}
   172  
   173  	if !c.noLock {
   174  		if c.flock != nil {
   175  			if err := c.flock.unlock(); err != nil {
   176  				return err
   177  			}
   178  		}
   179  	}
   180  
   181  	if c.wfd != nil {
   182  		if err := c.wfd.Close(); err != nil {
   183  			return err
   184  		}
   185  		c.wfd = nil
   186  	}
   187  
   188  	return nil
   189  }