"Fossies" - the Fresh Open Source Software Archive

Member "AdGuardHome-0.104.3/internal/querylog/qlog_reader.go" (19 Nov 2020, 3934 Bytes) of package /linux/misc/dns/AdGuardHome-0.104.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "qlog_reader.go": 0.104.1_vs_0.104.3.

    1 package querylog
    2 
    3 import (
    4     "errors"
    5     "fmt"
    6     "io"
    7 
    8     "github.com/AdguardTeam/AdGuardHome/internal/agherr"
    9 )
   10 
   11 // QLogReader allows reading from multiple query log files in the reverse order.
   12 //
   13 // Please note that this is a stateful object.
   14 // Internally, it contains a pointer to a particular query log file, and
   15 // to a specific position in this file, and it reads lines in reverse order
   16 // starting from that position.
   17 type QLogReader struct {
   18     // qFiles - array with the query log files
   19     // The order is - from oldest to newest
   20     qFiles []*QLogFile
   21 
   22     currentFile int // Index of the current file
   23 }
   24 
   25 // NewQLogReader initializes a QLogReader instance
   26 // with the specified files
   27 func NewQLogReader(files []string) (*QLogReader, error) {
   28     qFiles := make([]*QLogFile, 0)
   29 
   30     for _, f := range files {
   31         q, err := NewQLogFile(f)
   32         if err != nil {
   33             // Close what we've already opened
   34             _ = closeQFiles(qFiles)
   35             return nil, err
   36         }
   37 
   38         qFiles = append(qFiles, q)
   39     }
   40 
   41     return &QLogReader{
   42         qFiles:      qFiles,
   43         currentFile: (len(qFiles) - 1),
   44     }, nil
   45 }
   46 
   47 // Seek performs binary search of a query log record with the specified timestamp.
   48 // If the record is found, it sets QLogReader's position to point to that line,
   49 // so that the next ReadNext call returned this line.
   50 //
   51 // Returns nil if the record is successfully found.
   52 // Returns an error if for some reason we could not find a record with the specified timestamp.
   53 func (r *QLogReader) Seek(timestamp int64) (err error) {
   54     for i := len(r.qFiles) - 1; i >= 0; i-- {
   55         q := r.qFiles[i]
   56         _, _, err = q.Seek(timestamp)
   57         if err == nil {
   58             // Search is finished, and the searched element have
   59             // been found. Update currentFile only, position is
   60             // already set properly in QLogFile.
   61             r.currentFile = i
   62 
   63             return nil
   64         } else if errors.Is(err, ErrTSTooEarly) {
   65             // Look at the next file, since we've reached the end of
   66             // this one.
   67             continue
   68         } else if errors.Is(err, ErrTSTooLate) {
   69             // Just seek to the start then.  timestamp is probably
   70             // between the end of the previous one and the start of
   71             // this one.
   72             return r.SeekStart()
   73         } else if errors.Is(err, ErrTSNotFound) {
   74             break
   75         }
   76     }
   77 
   78     return fmt.Errorf("querylog: %w", err)
   79 }
   80 
   81 // SeekStart changes the current position to the end of the newest file
   82 // Please note that we're reading query log in the reverse order
   83 // and that's why log start is actually the end of file
   84 //
   85 // Returns nil if we were able to change the current position.
   86 // Returns error in any other case.
   87 func (r *QLogReader) SeekStart() error {
   88     if len(r.qFiles) == 0 {
   89         return nil
   90     }
   91 
   92     r.currentFile = len(r.qFiles) - 1
   93     _, err := r.qFiles[r.currentFile].SeekStart()
   94     return err
   95 }
   96 
   97 // ReadNext reads the next line (in the reverse order) from the query log files.
   98 // and shifts the current position left to the next (actually prev) line (or the next file).
   99 // returns io.EOF if there's nothing to read more.
  100 func (r *QLogReader) ReadNext() (string, error) {
  101     if len(r.qFiles) == 0 {
  102         return "", io.EOF
  103     }
  104 
  105     for r.currentFile >= 0 {
  106         q := r.qFiles[r.currentFile]
  107         line, err := q.ReadNext()
  108         if err != nil {
  109             // Shift to the older file
  110             r.currentFile--
  111             if r.currentFile < 0 {
  112                 break
  113             }
  114 
  115             q = r.qFiles[r.currentFile]
  116 
  117             // Set it's position to the start right away
  118             _, err = q.SeekStart()
  119 
  120             // This is unexpected, return an error right away
  121             if err != nil {
  122                 return "", err
  123             }
  124         } else {
  125             return line, nil
  126         }
  127     }
  128 
  129     // Nothing to read anymore
  130     return "", io.EOF
  131 }
  132 
  133 // Close closes the QLogReader
  134 func (r *QLogReader) Close() error {
  135     return closeQFiles(r.qFiles)
  136 }
  137 
  138 // closeQFiles - helper method to close multiple QLogFile instances
  139 func closeQFiles(qFiles []*QLogFile) error {
  140     var errs []error
  141 
  142     for _, q := range qFiles {
  143         err := q.Close()
  144         if err != nil {
  145             errs = append(errs, err)
  146         }
  147     }
  148 
  149     if len(errs) > 0 {
  150         return agherr.Many("Error while closing QLogReader", errs...)
  151     }
  152 
  153     return nil
  154 }