github.com/GuanceCloud/cliutils@v1.1.21/diskcache/rotate.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  	"encoding/binary"
    10  	"fmt"
    11  	"os"
    12  	"path/filepath"
    13  	"sort"
    14  	"strconv"
    15  	"strings"
    16  )
    17  
    18  // Rotate force diskcache switch(rotate) from current write file(cwf) to next
    19  // new file, leave cwf become readble on successive Get().
    20  //
    21  // NOTE: You do not need to call Rotate() during daily usage, we export
    22  // that function for testing cases.
    23  func (c *DiskCache) Rotate() error {
    24  	return c.rotate()
    25  }
    26  
    27  // rotate to next new file, append to reading list.
    28  func (c *DiskCache) rotate() error {
    29  	c.rwlock.Lock()
    30  	defer c.rwlock.Unlock()
    31  
    32  	defer func() {
    33  		rotateVec.WithLabelValues(c.path).Inc()
    34  		sizeVec.WithLabelValues(c.path).Set(float64(c.size))
    35  		datafilesVec.WithLabelValues(c.path).Set(float64(len(c.dataFiles)))
    36  	}()
    37  
    38  	eof := make([]byte, dataHeaderLen)
    39  	binary.LittleEndian.PutUint32(eof, EOFHint)
    40  	if _, err := c.wfd.Write(eof); err != nil { // append EOF to file end
    41  		return err
    42  	}
    43  
    44  	// NOTE: EOF bytes do not count to size
    45  
    46  	// rotate file
    47  	var newfile string
    48  	if len(c.dataFiles) == 0 {
    49  		newfile = filepath.Join(c.path, fmt.Sprintf("data.%032d", 0)) // first rotate file
    50  	} else {
    51  		// parse last file's name, such as `data.000003', the new rotate file is `data.000004`
    52  		last := c.dataFiles[len(c.dataFiles)-1]
    53  		arr := strings.Split(filepath.Base(last), ".")
    54  		if len(arr) != 2 {
    55  			return ErrInvalidDataFileName
    56  		}
    57  		x, err := strconv.ParseInt(arr[1], 10, 64)
    58  		if err != nil {
    59  			return ErrInvalidDataFileNameSuffix
    60  		}
    61  
    62  		// data.0003 -> data.0004
    63  		newfile = filepath.Join(c.path, fmt.Sprintf("data.%032d", x+1))
    64  	}
    65  
    66  	// close current writing file
    67  	if err := c.wfd.Close(); err != nil {
    68  		return err
    69  	}
    70  	c.wfd = nil
    71  
    72  	// rename data -> data.0004
    73  	if err := os.Rename(c.curWriteFile, newfile); err != nil {
    74  		return err
    75  	}
    76  
    77  	c.dataFiles = append(c.dataFiles, newfile)
    78  	sort.Strings(c.dataFiles)
    79  
    80  	// reopen new write file
    81  	if err := c.openWriteFile(); err != nil {
    82  		return err
    83  	}
    84  
    85  	return nil
    86  }
    87  
    88  // after file read on EOF, remove the file.
    89  func (c *DiskCache) removeCurrentReadingFile() error {
    90  	c.rwlock.Lock()
    91  	defer c.rwlock.Unlock()
    92  
    93  	defer func() {
    94  		sizeVec.WithLabelValues(c.path).Set(float64(c.size))
    95  		removeVec.WithLabelValues(c.path).Inc()
    96  		datafilesVec.WithLabelValues(c.path).Set(float64(len(c.dataFiles)))
    97  	}()
    98  
    99  	if c.rfd != nil {
   100  		if err := c.rfd.Close(); err != nil {
   101  			return err
   102  		}
   103  		c.rfd = nil
   104  	}
   105  
   106  	if fi, err := os.Stat(c.curReadfile); err == nil { // file exist
   107  		if fi.Size() > dataHeaderLen {
   108  			c.size -= (fi.Size() - dataHeaderLen) // EOF bytes do not counted in size
   109  		}
   110  
   111  		if err := os.Remove(c.curReadfile); err != nil {
   112  			return fmt.Errorf("removeCurrentReadingFile: %q: %w", c.curReadfile, err)
   113  		}
   114  	}
   115  
   116  	c.curReadfile = ""
   117  
   118  	if len(c.dataFiles) > 0 {
   119  		c.dataFiles = c.dataFiles[1:] // first file removed
   120  	}
   121  
   122  	return nil
   123  }