github.com/GuanceCloud/cliutils@v1.1.21/diskcache/get.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 "io" 12 "time" 13 ) 14 15 // Fn is the handler to eat cache from diskcache. 16 type Fn func([]byte) error 17 18 func (c *DiskCache) switchNextFile() error { 19 if c.curReadfile != "" { 20 if err := c.removeCurrentReadingFile(); err != nil { 21 return fmt.Errorf("removeCurrentReadingFile: %w", err) 22 } 23 } 24 25 // clear .pos 26 if !c.noPos { 27 if err := c.pos.reset(); err != nil { 28 return err 29 } 30 } 31 32 // reopen next file to read 33 return c.doSwitchNextFile() 34 } 35 36 func (c *DiskCache) skipBadFile() error { 37 defer func() { 38 droppedBatchVec.WithLabelValues(c.path, reasonBadDataFile).Inc() 39 }() 40 41 return c.switchNextFile() 42 } 43 44 // Get fetch new data from disk cache, then passing to fn 45 // if any error occurred during call fn, the reading data is 46 // dropped, and will not read again. 47 // 48 // Get is safe to call concurrently with other operations and will 49 // block until all other operations finish. 50 func (c *DiskCache) Get(fn Fn) error { 51 var ( 52 n, nbytes int 53 err error 54 ) 55 56 c.rlock.Lock() 57 defer c.rlock.Unlock() 58 59 start := time.Now() 60 61 defer func() { 62 if uint32(nbytes) != EOFHint { 63 getBytesVec.WithLabelValues(c.path).Add(float64(nbytes)) 64 65 // get on EOF not counted as a real Get 66 getVec.WithLabelValues(c.path).Inc() 67 getLatencyVec.WithLabelValues(c.path).Observe(float64(time.Since(start) / time.Microsecond)) 68 } 69 }() 70 71 // wakeup sleeping write file, rotate it for succession reading! 72 if time.Since(c.wfdLastWrite) > c.wakeup && c.curBatchSize > 0 { 73 wakeupVec.WithLabelValues(c.path).Inc() 74 75 if err = func() error { 76 c.wlock.Lock() 77 defer c.wlock.Unlock() 78 return c.rotate() 79 }(); err != nil { 80 return err 81 } 82 } 83 84 if c.rfd == nil { 85 if err = c.switchNextFile(); err != nil { 86 return err 87 } 88 } 89 90 retry: 91 if c.rfd == nil { 92 return ErrEOF 93 } 94 95 hdr := make([]byte, dataHeaderLen) 96 if n, err = c.rfd.Read(hdr); err != nil || n != dataHeaderLen { 97 // 98 // On bad datafile, just ignore and delete the file. 99 // 100 if err = c.skipBadFile(); err != nil { 101 return err 102 } 103 104 goto retry // read next new file to save another Get() calling. 105 } 106 107 // how many bytes of current data? 108 nbytes = int(binary.LittleEndian.Uint32(hdr[0:])) 109 110 if uint32(nbytes) == EOFHint { // EOF 111 if err := c.switchNextFile(); err != nil { 112 return fmt.Errorf("switchNextFile: %w", err) 113 } 114 115 goto retry // read next new file to save another Get() calling. 116 } 117 118 databuf := make([]byte, nbytes) 119 120 if n, err = c.rfd.Read(databuf); err != nil { 121 return err 122 } else if n != nbytes { 123 return ErrUnexpectedReadSize 124 } 125 126 if fn == nil { 127 goto __updatePos 128 } 129 130 if err = fn(databuf); err != nil { 131 // seek back 132 if !c.noFallbackOnError { 133 if _, serr := c.rfd.Seek(-int64(dataHeaderLen+nbytes), io.SeekCurrent); serr != nil { 134 return serr 135 } 136 137 seekBackVec.WithLabelValues(c.path).Inc() 138 goto __end // do not update .pos 139 } 140 } 141 142 __updatePos: 143 // update seek position 144 if !c.noPos { 145 c.pos.Seek += int64(dataHeaderLen + nbytes) 146 if derr := c.pos.dumpFile(); derr != nil { 147 return derr 148 } 149 } 150 151 __end: 152 return err 153 }