github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/indexshipper/shipper.go (about) 1 package indexshipper 2 3 import ( 4 "context" 5 "flag" 6 "fmt" 7 "sync" 8 "time" 9 10 "github.com/go-kit/log/level" 11 "github.com/prometheus/client_golang/prometheus" 12 13 "github.com/grafana/loki/pkg/storage/chunk/client" 14 "github.com/grafana/loki/pkg/storage/config" 15 "github.com/grafana/loki/pkg/storage/stores/indexshipper/downloads" 16 "github.com/grafana/loki/pkg/storage/stores/indexshipper/gatewayclient" 17 "github.com/grafana/loki/pkg/storage/stores/indexshipper/index" 18 "github.com/grafana/loki/pkg/storage/stores/indexshipper/storage" 19 "github.com/grafana/loki/pkg/storage/stores/indexshipper/uploads" 20 util_log "github.com/grafana/loki/pkg/util/log" 21 ) 22 23 type Mode string 24 25 const ( 26 // ModeReadWrite is to allow both read and write 27 ModeReadWrite = Mode("RW") 28 // ModeReadOnly is to allow only read operations 29 ModeReadOnly = Mode("RO") 30 // ModeWriteOnly is to allow only write operations 31 ModeWriteOnly = Mode("WO") 32 33 // FilesystemObjectStoreType holds the periodic config type for the filesystem store 34 FilesystemObjectStoreType = "filesystem" 35 36 // UploadInterval defines interval for when we check if there are new index files to upload. 37 // It's also used to snapshot the currently written index tables so the snapshots can be used for reads. 38 UploadInterval = 1 * time.Minute 39 ) 40 41 type Index interface { 42 Close() error 43 } 44 45 type IndexShipper interface { 46 // AddIndex adds an immutable index to a logical table which would eventually get uploaded to the object store. 47 AddIndex(tableName, userID string, index index.Index) error 48 // ForEach lets us iterates through each index file in a table for a specific user. 49 // On the write path, it would iterate on the files given to the shipper for uploading, until they eventually get dropped from local disk. 50 // On the read path, it would iterate through the files if already downloaded else it would download and iterate through them. 51 ForEach(ctx context.Context, tableName, userID string, callback index.ForEachIndexCallback) error 52 Stop() 53 } 54 55 type Config struct { 56 ActiveIndexDirectory string `yaml:"active_index_directory"` 57 SharedStoreType string `yaml:"shared_store"` 58 SharedStoreKeyPrefix string `yaml:"shared_store_key_prefix"` 59 CacheLocation string `yaml:"cache_location"` 60 CacheTTL time.Duration `yaml:"cache_ttl"` 61 ResyncInterval time.Duration `yaml:"resync_interval"` 62 QueryReadyNumDays int `yaml:"query_ready_num_days"` 63 IndexGatewayClientConfig gatewayclient.IndexGatewayClientConfig `yaml:"index_gateway_client"` 64 65 IngesterName string 66 Mode Mode 67 IngesterDBRetainPeriod time.Duration 68 } 69 70 // RegisterFlagsWithPrefix registers flags. 71 func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 72 cfg.IndexGatewayClientConfig.RegisterFlagsWithPrefix(prefix+".shipper.index-gateway-client", f) 73 74 f.StringVar(&cfg.ActiveIndexDirectory, prefix+".shipper.active-index-directory", "", "Directory where ingesters would write index files which would then be uploaded by shipper to configured storage") 75 f.StringVar(&cfg.SharedStoreType, prefix+".shipper.shared-store", "", "Shared store for keeping index files. Supported types: gcs, s3, azure, filesystem") 76 f.StringVar(&cfg.SharedStoreKeyPrefix, prefix+".shipper.shared-store.key-prefix", "index/", "Prefix to add to Object Keys in Shared store. Path separator(if any) should always be a '/'. Prefix should never start with a separator but should always end with it") 77 f.StringVar(&cfg.CacheLocation, prefix+".shipper.cache-location", "", "Cache location for restoring index files from storage for queries") 78 f.DurationVar(&cfg.CacheTTL, prefix+".shipper.cache-ttl", 24*time.Hour, "TTL for index files restored in cache for queries") 79 f.DurationVar(&cfg.ResyncInterval, prefix+".shipper.resync-interval", 5*time.Minute, "Resync downloaded files with the storage") 80 f.IntVar(&cfg.QueryReadyNumDays, prefix+".shipper.query-ready-num-days", 0, "Number of days of common index to be kept downloaded for queries. For per tenant index query readiness, use limits overrides config.") 81 } 82 83 func (cfg *Config) Validate() error { 84 // set the default value for mode 85 if cfg.Mode == "" { 86 cfg.Mode = ModeReadWrite 87 } 88 return storage.ValidateSharedStoreKeyPrefix(cfg.SharedStoreKeyPrefix) 89 } 90 91 type indexShipper struct { 92 cfg Config 93 openIndexFileFunc index.OpenIndexFileFunc 94 uploadsManager uploads.TableManager 95 downloadsManager downloads.TableManager 96 97 stopOnce sync.Once 98 } 99 100 // NewIndexShipper creates a shipper for providing index store functionality using index files and object storage. 101 // It manages the whole life cycle of uploading the index and downloading the index at query time. 102 // 103 // Since IndexShipper is generic, which means it can be used to manage various index types under the same object storage and/or local disk path, 104 // it accepts ranges of table numbers(config.TableRanges) to be managed by the shipper. 105 // This is mostly useful on the read path to sync and manage specific index tables within the given table number ranges. 106 func NewIndexShipper(cfg Config, storageClient client.ObjectClient, limits downloads.Limits, 107 ownsTenantFn downloads.IndexGatewayOwnsTenant, open index.OpenIndexFileFunc, tableRangesToHandle config.TableRanges, reg prometheus.Registerer) (IndexShipper, error) { 108 switch cfg.Mode { 109 case ModeReadOnly, ModeWriteOnly, ModeReadWrite: 110 default: 111 return nil, fmt.Errorf("invalid mode: %v", cfg.Mode) 112 } 113 shipper := indexShipper{ 114 cfg: cfg, 115 openIndexFileFunc: open, 116 } 117 118 err := shipper.init(storageClient, limits, ownsTenantFn, tableRangesToHandle, reg) 119 if err != nil { 120 return nil, err 121 } 122 123 level.Info(util_log.Logger).Log("msg", fmt.Sprintf("starting index shipper in %s mode", cfg.Mode)) 124 125 return &shipper, nil 126 } 127 128 func (s *indexShipper) init(storageClient client.ObjectClient, limits downloads.Limits, 129 ownsTenantFn downloads.IndexGatewayOwnsTenant, tableRangesToHandle config.TableRanges, reg prometheus.Registerer) error { 130 indexStorageClient := storage.NewIndexStorageClient(storageClient, s.cfg.SharedStoreKeyPrefix) 131 132 if s.cfg.Mode != ModeReadOnly { 133 cfg := uploads.Config{ 134 UploadInterval: UploadInterval, 135 DBRetainPeriod: s.cfg.IngesterDBRetainPeriod, 136 } 137 uploadsManager, err := uploads.NewTableManager(cfg, indexStorageClient, reg) 138 if err != nil { 139 return err 140 } 141 142 s.uploadsManager = uploadsManager 143 } 144 145 if s.cfg.Mode != ModeWriteOnly { 146 cfg := downloads.Config{ 147 CacheDir: s.cfg.CacheLocation, 148 SyncInterval: s.cfg.ResyncInterval, 149 CacheTTL: s.cfg.CacheTTL, 150 QueryReadyNumDays: s.cfg.QueryReadyNumDays, 151 Limits: limits, 152 } 153 downloadsManager, err := downloads.NewTableManager(cfg, s.openIndexFileFunc, indexStorageClient, ownsTenantFn, tableRangesToHandle, reg) 154 if err != nil { 155 return err 156 } 157 158 s.downloadsManager = downloadsManager 159 } 160 161 return nil 162 } 163 164 func (s *indexShipper) AddIndex(tableName, userID string, index index.Index) error { 165 return s.uploadsManager.AddIndex(tableName, userID, index) 166 } 167 168 func (s *indexShipper) ForEach(ctx context.Context, tableName, userID string, callback index.ForEachIndexCallback) error { 169 if s.downloadsManager != nil { 170 if err := s.downloadsManager.ForEach(ctx, tableName, userID, callback); err != nil { 171 return err 172 } 173 } 174 175 if s.uploadsManager != nil { 176 if err := s.uploadsManager.ForEach(tableName, userID, callback); err != nil { 177 return err 178 } 179 } 180 181 return nil 182 } 183 184 func (s *indexShipper) Stop() { 185 s.stopOnce.Do(s.stop) 186 } 187 188 func (s *indexShipper) stop() { 189 if s.uploadsManager != nil { 190 s.uploadsManager.Stop() 191 } 192 193 if s.downloadsManager != nil { 194 s.downloadsManager.Stop() 195 } 196 }