github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/cmd/mount/test/seekers.go (about)

     1  // +build ignore
     2  
     3  // Read lots files with lots of simultaneous seeking to stress test the seek code
     4  package main
     5  
     6  import (
     7  	"flag"
     8  	"io"
     9  	"log"
    10  	"math/rand"
    11  	"os"
    12  	"path/filepath"
    13  	"sort"
    14  	"sync"
    15  	"time"
    16  )
    17  
    18  var (
    19  	// Flags
    20  	iterations   = flag.Int("n", 1e6, "Iterations to try")
    21  	maxBlockSize = flag.Int("b", 1024*1024, "Max block size to read")
    22  	simultaneous = flag.Int("transfers", 16, "Number of simultaneous files to open")
    23  	seeksPerFile = flag.Int("seeks", 8, "Seeks per file")
    24  	mask         = flag.Int64("mask", 0, "mask for seek, eg 0x7fff")
    25  )
    26  
    27  func init() {
    28  	rand.Seed(time.Now().UnixNano())
    29  }
    30  
    31  func seekTest(n int, file string) {
    32  	in, err := os.Open(file)
    33  	if err != nil {
    34  		log.Fatalf("Couldn't open %q: %v", file, err)
    35  	}
    36  	fi, err := in.Stat()
    37  	if err != nil {
    38  		log.Fatalf("Couldn't stat %q: %v", file, err)
    39  	}
    40  	size := fi.Size()
    41  
    42  	// FIXME make sure we try start and end
    43  
    44  	maxBlockSize := *maxBlockSize
    45  	if int64(maxBlockSize) > size {
    46  		maxBlockSize = int(size)
    47  	}
    48  	for i := 0; i < n; i++ {
    49  		start := rand.Int63n(size)
    50  		if *mask != 0 {
    51  			start &^= *mask
    52  		}
    53  		blockSize := rand.Intn(maxBlockSize)
    54  		beyondEnd := false
    55  		switch rand.Intn(10) {
    56  		case 0:
    57  			start = 0
    58  		case 1:
    59  			start = size - int64(blockSize)
    60  		case 2:
    61  			// seek beyond the end
    62  			start = size + int64(blockSize)
    63  			beyondEnd = true
    64  		default:
    65  		}
    66  		if !beyondEnd && int64(blockSize) > size-start {
    67  			blockSize = int(size - start)
    68  		}
    69  		log.Printf("%s: Reading %d from %d", file, blockSize, start)
    70  
    71  		_, err = in.Seek(start, io.SeekStart)
    72  		if err != nil {
    73  			log.Fatalf("Seek failed on %q: %v", file, err)
    74  		}
    75  
    76  		buf := make([]byte, blockSize)
    77  		n, err := io.ReadFull(in, buf)
    78  		if beyondEnd && err == io.EOF {
    79  			// OK
    80  		} else if err != nil {
    81  			log.Fatalf("Read failed on %q: %v (%d)", file, err, n)
    82  		}
    83  	}
    84  
    85  	err = in.Close()
    86  	if err != nil {
    87  		log.Fatalf("Error closing %q: %v", file, err)
    88  	}
    89  }
    90  
    91  // Find all the files in dir
    92  func findFiles(dir string) (files []string) {
    93  	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
    94  		if info.Mode().IsRegular() && info.Size() > 0 {
    95  			files = append(files, path)
    96  		}
    97  		return nil
    98  	})
    99  	sort.Strings(files)
   100  	return files
   101  }
   102  
   103  func main() {
   104  	flag.Parse()
   105  	args := flag.Args()
   106  	if len(args) != 1 {
   107  		log.Fatalf("Require a directory as argument")
   108  	}
   109  	dir := args[0]
   110  	files := findFiles(dir)
   111  	jobs := make(chan string, *simultaneous)
   112  	var wg sync.WaitGroup
   113  	wg.Add(*simultaneous)
   114  	for i := 0; i < *simultaneous; i++ {
   115  		go func() {
   116  			defer wg.Done()
   117  			for file := range jobs {
   118  				seekTest(*seeksPerFile, file)
   119  			}
   120  		}()
   121  	}
   122  	for i := 0; i < *iterations; i++ {
   123  		i := rand.Intn(len(files))
   124  		jobs <- files[i]
   125  		//jobs <- files[i]
   126  	}
   127  	close(jobs)
   128  	wg.Wait()
   129  }