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 }