func (f *FileStore) TSMReader(path string) *TSMReader {
	f.mu.RLock()
	defer f.mu.RUnlock()
	for _, r := range f.files {
		if r.Path() == path {
			return r.(*TSMReader)
		}
	}
	return nil
}
Reader returns a TSMReader for path if one is currently managed by the FileStore.
Otherwise it returns nil.
package tsm1
import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"math"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
	"github.com/influxdata/influxdb/models"
	"github.com/influxdata/influxdb/pkg/limiter"
	"github.com/influxdata/influxdb/pkg/metrics"
	"github.com/influxdata/influxdb/query"
	"go.uber.org/zap"
)
const (
	TmpTSMFileExtension = "tmp"
	BadTSMFileExtension = "bad"
)
type TSMFile interface {
	Path() string
	Read(key []byte, t int64) ([]Value, error)
	ReadAt(entry *IndexEntry, values []Value) ([]Value, error)
	ReadFloatBlockAt(entry *IndexEntry, values *[]FloatValue) ([]FloatValue, error)
	ReadIntegerBlockAt(entry *IndexEntry, values *[]IntegerValue) ([]IntegerValue, error)
	ReadUnsignedBlockAt(entry *IndexEntry, values *[]UnsignedValue) ([]UnsignedValue, error)
	ReadStringBlockAt(entry *IndexEntry, values *[]StringValue) ([]StringValue, error)
	ReadBooleanBlockAt(entry *IndexEntry, values *[]BooleanValue) ([]BooleanValue, error)
	Entries(key []byte) []IndexEntry
	ReadEntries(key []byte, entries *[]IndexEntry) []IndexEntry
	ContainsValue(key []byte, t int64) bool
	Contains(key []byte) bool
	OverlapsTimeRange(min, max int64) bool
	OverlapsKeyRange(min, max []byte) bool
	TimeRange() (int64, int64)
	TombstoneRange(key []byte) []TimeRange
	KeyRange() ([]byte, []byte)
	KeyCount() int
	Seek(key []byte) int
	KeyAt(idx int) ([]byte, byte)
	Type(key []byte) (byte, error)
	BatchDelete() BatchDeleter
	Delete(keys [][]byte) error
	DeleteRange(keys [][]byte, min, max int64) error
	HasTombstones() bool
	TombstoneFiles() []FileStat
	Close() error
	Size() uint32
	Rename(path string) error
	Remove() error
	InUse() bool
	Ref()
	Unref()
	Stats() FileStat
	BlockIterator() *BlockIterator
	Free() error
}
const (
	statFileStoreBytes = "diskBytes"
	statFileStoreCount = "numFiles"
)
var (
	floatBlocksDecodedCounter    = metrics.MustRegisterCounter("float_blocks_decoded", metrics.WithGroup(tsmGroup))
	floatBlocksSizeCounter       = metrics.MustRegisterCounter("float_blocks_size_bytes", metrics.WithGroup(tsmGroup))
	integerBlocksDecodedCounter  = metrics.MustRegisterCounter("integer_blocks_decoded", metrics.WithGroup(tsmGroup))
	integerBlocksSizeCounter     = metrics.MustRegisterCounter("integer_blocks_size_bytes", metrics.WithGroup(tsmGroup))
	unsignedBlocksDecodedCounter = metrics.MustRegisterCounter("unsigned_blocks_decoded", metrics.WithGroup(tsmGroup))
	unsignedBlocksSizeCounter    = metrics.MustRegisterCounter("unsigned_blocks_size_bytes", metrics.WithGroup(tsmGroup))
	stringBlocksDecodedCounter   = metrics.MustRegisterCounter("string_blocks_decoded", metrics.WithGroup(tsmGroup))
	stringBlocksSizeCounter      = metrics.MustRegisterCounter("string_blocks_size_bytes", metrics.WithGroup(tsmGroup))
	booleanBlocksDecodedCounter  = metrics.MustRegisterCounter("boolean_blocks_decoded", metrics.WithGroup(tsmGroup))
	booleanBlocksSizeCounter     = metrics.MustRegisterCounter("boolean_blocks_size_bytes", metrics.WithGroup(tsmGroup))
)
type FileStore struct {
	mu           sync.RWMutex
	lastModified time.Time
	lastFileStats []FileStat
	currentGeneration int
	dir               string
	files []TSMFile
	logger       *zap.Logger  
	traceLogger  *zap.Logger  
	traceLogging bool
	stats  *FileStoreStatistics
	purger *purger
	currentTempDirID int
}
type FileStat struct {
	Path             string
	HasTombstone     bool
	Size             uint32
	LastModified     int64
	MinTime, MaxTime int64
	MinKey, MaxKey   []byte
}
func (f FileStat) OverlapsTimeRange(min, max int64) bool {
	return f.MinTime <= max && f.MaxTime >= min
}
func (f FileStat) OverlapsKeyRange(min, max []byte) bool {
	return len(min) != 0 && len(max) != 0 && bytes.Compare(f.MinKey, max) <= 0 && bytes.Compare(f.MaxKey, min) >= 0
}
func (f FileStat) ContainsKey(key []byte) bool {
	return bytes.Compare(f.MinKey, key) >= 0 || bytes.Compare(key, f.MaxKey) <= 0
}
func NewFileStore(dir string) *FileStore {
	logger := zap.NewNop()
	fs := &FileStore{
		dir:          dir,
		lastModified: time.Time{},
		logger:       logger,
		traceLogger:  logger,
		stats:        &FileStoreStatistics{},
		purger: &purger{
			files:  map[string]TSMFile{},
			logger: logger,
		},
	}
	fs.purger.fileStore = fs
	return fs
}
func (f *FileStore) enableTraceLogging(enabled bool) {
	f.traceLogging = enabled
	if enabled {
		f.traceLogger = f.logger
	}
}
func (f *FileStore) WithLogger(log *zap.Logger) {
	f.logger = log.With(zap.String("service", "filestore"))
	f.purger.logger = f.logger
	if f.traceLogging {
		f.traceLogger = f.logger
	}
}
type FileStoreStatistics struct {
	DiskBytes int64
	FileCount int64
}
func (f *FileStore) Statistics(tags map[string]string) []models.Statistic {
	return []models.Statistic{{
		Name: "tsm1_filestore",
		Tags: tags,
		Values: map[string]interface{}{
			statFileStoreBytes: atomic.LoadInt64(&f.stats.DiskBytes),
			statFileStoreCount: atomic.LoadInt64(&f.stats.FileCount),
		},
	}}
}
func (f *FileStore) Count() int {
	f.mu.RLock()
	defer f.mu.RUnlock()
	return len(f.files)
}
func (f *FileStore) Files() []TSMFile {
	f.mu.RLock()
	defer f.mu.RUnlock()
	return f.files
}
func (f *FileStore) Free() error {
	f.mu.RLock()
	defer f.mu.RUnlock()
	for _, f := range f.files {
		if err := f.Free(); err != nil {
			return err
		}
	}
	return nil
}
func (f *FileStore) CurrentGeneration() int {
	f.mu.RLock()
	defer f.mu.RUnlock()
	return f.currentGeneration
}
func (f *FileStore) NextGeneration() int {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.currentGeneration++
	return f.currentGeneration
}
func (f *FileStore) WalkKeys(seek []byte, fn func(key []byte, typ byte) error) error {
	f.mu.RLock()
	if len(f.files) == 0 {
		f.mu.RUnlock()
		return nil
	}
	ki := newMergeKeyIterator(f.files, seek)
	f.mu.RUnlock()
	for ki.Next() {
		key, typ := ki.Read()
		if err := fn(key, typ); err != nil {
			return err
		}
	}
	return nil
}
func (f *FileStore) Keys() map[string]byte {
	f.mu.RLock()
	defer f.mu.RUnlock()
	uniqueKeys := map[string]byte{}
	if err := f.WalkKeys(nil, func(key []byte, typ byte) error {
		uniqueKeys[string(key)] = typ
		return nil
	}); err != nil {
		return nil
	}
	return uniqueKeys
}
func (f *FileStore) Type(key []byte) (byte, error) {
	f.mu.RLock()
	defer f.mu.RUnlock()
	for _, f := range f.files {
		if f.Contains(key) {
			return f.Type(key)
		}
	}
	return 0, fmt.Errorf("unknown type for %v", key)
}
func (f *FileStore) Delete(keys [][]byte) error {
	return f.DeleteRange(keys, math.MinInt64, math.MaxInt64)
}
func (f *FileStore) Apply(fn func(r TSMFile) error) error {
	limiter := limiter.NewFixed(runtime.GOMAXPROCS(0))
	f.mu.RLock()
	errC := make(chan error, len(f.files))
	for _, f := range f.files {
		go func(r TSMFile) {
			limiter.Take()
			defer limiter.Release()
			r.Ref()
			defer r.Unref()
			errC <- fn(r)
		}(f)
	}
	var applyErr error
	for i := 0; i < cap(errC); i++ {
		if err := <-errC; err != nil {
			applyErr = err
		}
	}
	f.mu.RUnlock()
	f.mu.Lock()
	f.lastModified = time.Now().UTC()
	f.lastFileStats = nil
	f.mu.Unlock()
	return applyErr
}
func (f *FileStore) DeleteRange(keys [][]byte, min, max int64) error {
	var batches BatchDeleters
	f.mu.RLock()
	for _, f := range f.files {
		if f.OverlapsTimeRange(min, max) {
			batches = append(batches, f.BatchDelete())
		}
	}
	f.mu.RUnlock()
	if len(batches) == 0 {
		return nil
	}
	if err := func() error {
		if err := batches.DeleteRange(keys, min, max); err != nil {
			return err
		}
		return batches.Commit()
	}(); err != nil {
		_ = batches.Rollback()
		return err
	}
	f.mu.Lock()
	f.lastModified = time.Now().UTC()
	f.lastFileStats = nil
	f.mu.Unlock()
	return nil
}
func (f *FileStore) Open() error {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.dir == "" {
		return nil
	}
	tmpfiles, err := ioutil.ReadDir(f.dir)
	if err != nil {
		return err
	}
	ext := fmt.Sprintf(".%s", TmpTSMFileExtension)
	for _, fi := range tmpfiles {
		if fi.IsDir() && strings.HasSuffix(fi.Name(), ext) {
			ss := strings.Split(filepath.Base(fi.Name()), ".")
			if len(ss) == 2 {
				if i, err := strconv.Atoi(ss[0]); err != nil {
					if i > f.currentTempDirID {
						f.currentTempDirID = i
					}
				}
			}
		}
	}
	files, err := filepath.Glob(filepath.Join(f.dir, fmt.Sprintf("*.%s", TSMFileExtension)))
	if err != nil {
		return err
	}
	type res struct {
		r   *TSMReader
		err error
	}
	readerC := make(chan *res)
	for i, fn := range files {
		generation, _, err := ParseTSMFileName(fn)
		if err != nil {
			return err
		}
		if generation >= f.currentGeneration {
			f.currentGeneration = generation + 1
		}
		file, err := os.OpenFile(fn, os.O_RDONLY, 0666)
		if err != nil {
			return fmt.Errorf("error opening file %s: %v", fn, err)
		}
		go func(idx int, file *os.File) {
			start := time.Now()
			df, err := NewTSMReader(file)
			f.logger.Info("Opened file",
				zap.String("path", file.Name()),
				zap.Int("id", idx),
				zap.Duration("duration", time.Since(start)))
			if err != nil {
				f.logger.Error("Cannot read corrupt tsm file, renaming", zap.String("path", file.Name()), zap.Int("id", idx), zap.Error(err))
				if e := os.Rename(file.Name(), file.Name()+"."+BadTSMFileExtension); e != nil {
					f.logger.Error("Cannot rename corrupt tsm file", zap.String("path", file.Name()), zap.Int("id", idx), zap.Error(e))
					readerC <- &res{r: df, err: fmt.Errorf("cannot rename corrupt file %s: %v", file.Name(), e)}
					return
				}
			}
			readerC <- &res{r: df}
		}(i, file)
	}
	var lm int64
	for range files {
		res := <-readerC
		if res.err != nil {
			return res.err
		} else if res.r == nil {
			continue
		}
		f.files = append(f.files, res.r)
		atomic.AddInt64(&f.stats.DiskBytes, int64(res.r.Size()))
		for _, ts := range res.r.TombstoneFiles() {
			atomic.AddInt64(&f.stats.DiskBytes, int64(ts.Size))
		}
		if res.r.LastModified() > lm {
			lm = res.r.LastModified()
		}
	}
	f.lastModified = time.Unix(0, lm).UTC()
	close(readerC)
	sort.Sort(tsmReaders(f.files))
	atomic.StoreInt64(&f.stats.FileCount, int64(len(f.files)))
	return nil
}
func (f *FileStore) Close() error {
	f.mu.Lock()
	files := f.files
	f.lastFileStats = nil
	f.files = nil
	atomic.StoreInt64(&f.stats.FileCount, 0)
	f.mu.Unlock()
	for _, file := range files {
		err := file.Close()
		if err != nil {
			return err
		}
	}
	return nil
}
func (f *FileStore) DiskSizeBytes() int64 {
	return atomic.LoadInt64(&f.stats.DiskBytes)
}
func (f *FileStore) Read(key []byte, t int64) ([]Value, error) {
	f.mu.RLock()
	defer f.mu.RUnlock()
	for _, f := range f.files {
		if !f.Contains(key) {
			continue
		}
		v, err := f.Read(key, t)
		if err != nil {
			return nil, err
		}
		if len(v) > 0 {
			return v, nil
		}
	}
	return nil, nil
}
func (f *FileStore) Cost(key []byte, min, max int64) query.IteratorCost {
	f.mu.RLock()
	defer f.mu.RUnlock()
	return f.cost(key, min, max)
}