github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/shipper/shipper_index_client.go (about) 1 package shipper 2 3 import ( 4 "context" 5 "flag" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "path" 10 "sync" 11 "time" 12 13 "github.com/go-kit/log/level" 14 "github.com/prometheus/client_golang/prometheus" 15 "github.com/weaveworks/common/instrument" 16 "go.etcd.io/bbolt" 17 18 "github.com/grafana/loki/pkg/storage/chunk/client" 19 "github.com/grafana/loki/pkg/storage/chunk/client/local" 20 "github.com/grafana/loki/pkg/storage/chunk/client/util" 21 "github.com/grafana/loki/pkg/storage/config" 22 "github.com/grafana/loki/pkg/storage/stores/indexshipper" 23 "github.com/grafana/loki/pkg/storage/stores/indexshipper/downloads" 24 series_index "github.com/grafana/loki/pkg/storage/stores/series/index" 25 "github.com/grafana/loki/pkg/storage/stores/shipper/index" 26 "github.com/grafana/loki/pkg/storage/stores/shipper/index/indexfile" 27 util_log "github.com/grafana/loki/pkg/util/log" 28 ) 29 30 type Config struct { 31 indexshipper.Config `yaml:",inline"` 32 BuildPerTenantIndex bool `yaml:"build_per_tenant_index"` 33 } 34 35 // RegisterFlags registers flags. 36 func (cfg *Config) RegisterFlags(f *flag.FlagSet) { 37 cfg.RegisterFlagsWithPrefix("boltdb", f) 38 39 } 40 41 func (cfg *Config) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { 42 cfg.Config.RegisterFlagsWithPrefix(prefix, f) 43 f.BoolVar(&cfg.BuildPerTenantIndex, prefix+"shipper.build-per-tenant-index", false, "Build per tenant index files") 44 } 45 46 func (cfg *Config) Validate() error { 47 return cfg.Config.Validate() 48 } 49 50 type writer interface { 51 ForEach(ctx context.Context, tableName string, callback func(boltdb *bbolt.DB) error) error 52 BatchWrite(ctx context.Context, batch series_index.WriteBatch) error 53 Stop() 54 } 55 56 type indexClient struct { 57 cfg Config 58 indexShipper indexshipper.IndexShipper 59 writer writer 60 querier index.Querier 61 62 metrics *metrics 63 stopOnce sync.Once 64 } 65 66 // NewShipper creates a shipper for syncing local objects with a store 67 func NewShipper(cfg Config, storageClient client.ObjectClient, limits downloads.Limits, 68 ownsTenantFn downloads.IndexGatewayOwnsTenant, tableRanges config.TableRanges, registerer prometheus.Registerer) (series_index.Client, error) { 69 i := indexClient{ 70 cfg: cfg, 71 metrics: newMetrics(registerer), 72 } 73 74 err := i.init(storageClient, limits, ownsTenantFn, tableRanges, registerer) 75 if err != nil { 76 return nil, err 77 } 78 79 level.Info(util_log.Logger).Log("msg", fmt.Sprintf("starting boltdb shipper in %s mode", cfg.Mode)) 80 81 return &i, nil 82 } 83 84 func (i *indexClient) init(storageClient client.ObjectClient, limits downloads.Limits, 85 ownsTenantFn downloads.IndexGatewayOwnsTenant, tableRanges config.TableRanges, registerer prometheus.Registerer) error { 86 var err error 87 i.indexShipper, err = indexshipper.NewIndexShipper(i.cfg.Config, storageClient, limits, ownsTenantFn, 88 indexfile.OpenIndexFile, tableRanges, prometheus.WrapRegistererWithPrefix("loki_boltdb_shipper_", registerer)) 89 if err != nil { 90 return err 91 } 92 93 if i.cfg.Mode != indexshipper.ModeReadOnly { 94 uploader, err := i.getUploaderName() 95 if err != nil { 96 return err 97 } 98 99 cfg := index.Config{ 100 Uploader: uploader, 101 IndexDir: i.cfg.ActiveIndexDirectory, 102 DBRetainPeriod: i.cfg.IngesterDBRetainPeriod, 103 MakePerTenantBuckets: i.cfg.BuildPerTenantIndex, 104 } 105 i.writer, err = index.NewTableManager(cfg, i.indexShipper, registerer) 106 if err != nil { 107 return err 108 } 109 } 110 111 i.querier = index.NewQuerier(i.writer, i.indexShipper) 112 113 return nil 114 } 115 116 func (i *indexClient) getUploaderName() (string, error) { 117 uploader := fmt.Sprintf("%s-%d", i.cfg.IngesterName, time.Now().UnixNano()) 118 119 uploaderFilePath := path.Join(i.cfg.ActiveIndexDirectory, "uploader", "name") 120 if err := util.EnsureDirectory(path.Dir(uploaderFilePath)); err != nil { 121 return "", err 122 } 123 124 _, err := os.Stat(uploaderFilePath) 125 if err != nil { 126 if !os.IsNotExist(err) { 127 return "", err 128 } 129 if err := ioutil.WriteFile(uploaderFilePath, []byte(uploader), 0o666); err != nil { 130 return "", err 131 } 132 } else { 133 ub, err := ioutil.ReadFile(uploaderFilePath) 134 if err != nil { 135 return "", err 136 } 137 uploader = string(ub) 138 } 139 140 return uploader, nil 141 } 142 143 func (i *indexClient) Stop() { 144 i.stopOnce.Do(i.stop) 145 } 146 147 func (i *indexClient) stop() { 148 if i.writer != nil { 149 i.writer.Stop() 150 } 151 i.indexShipper.Stop() 152 } 153 154 func (i *indexClient) NewWriteBatch() series_index.WriteBatch { 155 return local.NewWriteBatch() 156 } 157 158 func (i *indexClient) BatchWrite(ctx context.Context, batch series_index.WriteBatch) error { 159 return instrument.CollectedRequest(ctx, "WRITE", instrument.NewHistogramCollector(i.metrics.requestDurationSeconds), instrument.ErrorCode, func(ctx context.Context) error { 160 return i.writer.BatchWrite(ctx, batch) 161 }) 162 } 163 164 func (i *indexClient) QueryPages(ctx context.Context, queries []series_index.Query, callback series_index.QueryPagesCallback) error { 165 return instrument.CollectedRequest(ctx, "Shipper.Query", instrument.NewHistogramCollector(i.metrics.requestDurationSeconds), instrument.ErrorCode, func(ctx context.Context) error { 166 return i.querier.QueryPages(ctx, queries, callback) 167 }) 168 }