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 }