github.com/m3db/m3@v1.5.0/src/dbnode/storage/bootstrap/cache.go (about)

     1  // Copyright (c) 2020  Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package bootstrap
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  	"sync"
    27  
    28  	"github.com/m3db/m3/src/dbnode/namespace"
    29  	"github.com/m3db/m3/src/dbnode/persist"
    30  	"github.com/m3db/m3/src/dbnode/persist/fs"
    31  	"github.com/m3db/m3/src/x/instrument"
    32  )
    33  
    34  var (
    35  	errFilesystemOptsNotSet = errors.New("filesystemOptions not set")
    36  	errInstrumentOptsNotSet = errors.New("instrumentOptions not set")
    37  )
    38  
    39  type cache struct {
    40  	sync.Mutex
    41  
    42  	fsOpts               fs.Options
    43  	namespaceDetails     []NamespaceDetails
    44  	infoFilesByNamespace InfoFilesByNamespace
    45  	iOpts                instrument.Options
    46  	hasPopulatedInfo     bool
    47  }
    48  
    49  // NewCache creates a cache specifically to be used during the bootstrap process.
    50  // Primarily a mechanism for passing info files along without needing to re-read them at each
    51  // stage of the bootstrap process.
    52  func NewCache(options CacheOptions) (Cache, error) {
    53  	if err := options.Validate(); err != nil {
    54  		return nil, err
    55  	}
    56  	return &cache{
    57  		fsOpts:               options.FilesystemOptions(),
    58  		namespaceDetails:     options.NamespaceDetails(),
    59  		infoFilesByNamespace: make(InfoFilesByNamespace, len(options.NamespaceDetails())),
    60  		iOpts:                options.InstrumentOptions(),
    61  	}, nil
    62  }
    63  
    64  func (c *cache) InfoFilesForNamespace(ns namespace.Metadata) (InfoFileResultsPerShard, error) {
    65  	infoFilesByShard, ok := c.ReadInfoFiles()[ns]
    66  	// This should never happen as Cache object is initialized with all namespaces to bootstrap.
    67  	if !ok {
    68  		return nil, fmt.Errorf("attempting to read info files for namespace %v not specified at bootstrap "+
    69  			"startup", ns.ID().String())
    70  	}
    71  	return infoFilesByShard, nil
    72  }
    73  
    74  func (c *cache) InfoFilesForShard(ns namespace.Metadata, shard uint32) ([]fs.ReadInfoFileResult, error) {
    75  	infoFilesByShard, err := c.InfoFilesForNamespace(ns)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	infoFileResults, ok := infoFilesByShard[shard]
    80  	// This should never happen as Cache object is initialized with all shards to bootstrap.
    81  	if !ok {
    82  		return nil, fmt.Errorf("attempting to read info files for shard %v not specified "+
    83  			"at bootstrap startup for namespace %v", shard, ns.ID().String())
    84  	}
    85  	return infoFileResults, nil
    86  }
    87  
    88  func (c *cache) Evict() {
    89  	c.Lock()
    90  	defer c.Unlock()
    91  	c.hasPopulatedInfo = false
    92  }
    93  
    94  func (c *cache) ReadInfoFiles() InfoFilesByNamespace {
    95  	c.Lock()
    96  	defer c.Unlock()
    97  	if !c.hasPopulatedInfo {
    98  		c.populateInfoFilesByNamespaceWithLock()
    99  		c.hasPopulatedInfo = true
   100  	}
   101  	return c.infoFilesByNamespace
   102  }
   103  
   104  func (c *cache) populateInfoFilesByNamespaceWithLock() {
   105  	for _, finder := range c.namespaceDetails {
   106  		// NB(bodu): It is okay to reuse the info files by ns results per shard here
   107  		// as the shards were set in the cache ctor and do not change per invocation.
   108  		result, ok := c.infoFilesByNamespace[finder.Namespace]
   109  		if !ok {
   110  			result = make(InfoFileResultsPerShard, len(finder.Shards))
   111  		}
   112  		for _, shard := range finder.Shards {
   113  			result[shard] = fs.ReadInfoFiles(c.fsOpts.FilePathPrefix(),
   114  				finder.Namespace.ID(), shard, c.fsOpts.InfoReaderBufferSize(), c.fsOpts.DecodingOptions(),
   115  				persist.FileSetFlushType)
   116  		}
   117  
   118  		c.infoFilesByNamespace[finder.Namespace] = result
   119  	}
   120  }
   121  
   122  type cacheOptions struct {
   123  	fsOpts           fs.Options
   124  	namespaceDetails []NamespaceDetails
   125  	iOpts            instrument.Options
   126  }
   127  
   128  // NewCacheOptions creates new CacheOptions.
   129  func NewCacheOptions() CacheOptions {
   130  	return &cacheOptions{}
   131  }
   132  
   133  func (c *cacheOptions) Validate() error {
   134  	if c.fsOpts == nil {
   135  		return errFilesystemOptsNotSet
   136  	}
   137  	if err := c.fsOpts.Validate(); err != nil {
   138  		return err
   139  	}
   140  	if c.iOpts == nil {
   141  		return errInstrumentOptsNotSet
   142  	}
   143  	return nil
   144  }
   145  
   146  func (c *cacheOptions) SetFilesystemOptions(value fs.Options) CacheOptions {
   147  	opts := *c
   148  	opts.fsOpts = value
   149  	return &opts
   150  }
   151  
   152  func (c *cacheOptions) FilesystemOptions() fs.Options {
   153  	return c.fsOpts
   154  }
   155  
   156  func (c *cacheOptions) SetNamespaceDetails(value []NamespaceDetails) CacheOptions {
   157  	opts := *c
   158  	opts.namespaceDetails = value
   159  	return &opts
   160  }
   161  
   162  func (c *cacheOptions) NamespaceDetails() []NamespaceDetails {
   163  	return c.namespaceDetails
   164  }
   165  
   166  func (c *cacheOptions) SetInstrumentOptions(value instrument.Options) CacheOptions {
   167  	opts := *c
   168  	opts.iOpts = value
   169  	return &opts
   170  }
   171  
   172  func (c *cacheOptions) InstrumentOptions() instrument.Options {
   173  	return c.iOpts
   174  }