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  }