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 }