github.com/thanos-io/thanos@v0.32.5/cmd/thanos/store.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package main 5 6 import ( 7 "context" 8 "fmt" 9 "strconv" 10 "time" 11 12 "github.com/alecthomas/units" 13 extflag "github.com/efficientgo/tools/extkingpin" 14 "github.com/go-kit/log" 15 "github.com/go-kit/log/level" 16 grpclogging "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" 17 "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/tags" 18 "github.com/oklog/run" 19 "github.com/opentracing/opentracing-go" 20 "github.com/pkg/errors" 21 "github.com/prometheus/client_golang/prometheus" 22 commonmodel "github.com/prometheus/common/model" 23 "github.com/prometheus/common/route" 24 25 "github.com/thanos-io/objstore" 26 "github.com/thanos-io/objstore/client" 27 objstoretracing "github.com/thanos-io/objstore/tracing/opentracing" 28 29 blocksAPI "github.com/thanos-io/thanos/pkg/api/blocks" 30 "github.com/thanos-io/thanos/pkg/block" 31 "github.com/thanos-io/thanos/pkg/block/metadata" 32 "github.com/thanos-io/thanos/pkg/component" 33 hidden "github.com/thanos-io/thanos/pkg/extflag" 34 "github.com/thanos-io/thanos/pkg/extkingpin" 35 "github.com/thanos-io/thanos/pkg/extprom" 36 extpromhttp "github.com/thanos-io/thanos/pkg/extprom/http" 37 "github.com/thanos-io/thanos/pkg/gate" 38 "github.com/thanos-io/thanos/pkg/info" 39 "github.com/thanos-io/thanos/pkg/info/infopb" 40 "github.com/thanos-io/thanos/pkg/logging" 41 "github.com/thanos-io/thanos/pkg/model" 42 "github.com/thanos-io/thanos/pkg/prober" 43 "github.com/thanos-io/thanos/pkg/runutil" 44 grpcserver "github.com/thanos-io/thanos/pkg/server/grpc" 45 httpserver "github.com/thanos-io/thanos/pkg/server/http" 46 "github.com/thanos-io/thanos/pkg/store" 47 storecache "github.com/thanos-io/thanos/pkg/store/cache" 48 "github.com/thanos-io/thanos/pkg/store/labelpb" 49 "github.com/thanos-io/thanos/pkg/tls" 50 "github.com/thanos-io/thanos/pkg/ui" 51 ) 52 53 const ( 54 retryTimeoutDuration = 30 55 retryIntervalDuration = 10 56 ) 57 58 type storeConfig struct { 59 indexCacheConfigs extflag.PathOrContent 60 objStoreConfig extflag.PathOrContent 61 dataDir string 62 cacheIndexHeader bool 63 grpcConfig grpcConfig 64 httpConfig httpConfig 65 indexCacheSizeBytes units.Base2Bytes 66 chunkPoolSize units.Base2Bytes 67 estimatedMaxSeriesSize uint64 68 estimatedMaxChunkSize uint64 69 seriesBatchSize int 70 storeRateLimits store.SeriesSelectLimits 71 maxDownloadedBytes units.Base2Bytes 72 maxConcurrency int 73 component component.StoreAPI 74 debugLogging bool 75 syncInterval time.Duration 76 blockSyncConcurrency int 77 blockMetaFetchConcurrency int 78 filterConf *store.FilterConfig 79 selectorRelabelConf extflag.PathOrContent 80 advertiseCompatibilityLabel bool 81 consistencyDelay commonmodel.Duration 82 ignoreDeletionMarksDelay commonmodel.Duration 83 disableWeb bool 84 webConfig webConfig 85 label string 86 postingOffsetsInMemSampling int 87 cachingBucketConfig extflag.PathOrContent 88 reqLogConfig *extflag.PathOrContent 89 lazyIndexReaderEnabled bool 90 lazyIndexReaderIdleTimeout time.Duration 91 } 92 93 func (sc *storeConfig) registerFlag(cmd extkingpin.FlagClause) { 94 sc.httpConfig = *sc.httpConfig.registerFlag(cmd) 95 sc.grpcConfig = *sc.grpcConfig.registerFlag(cmd) 96 sc.storeRateLimits.RegisterFlags(cmd) 97 98 cmd.Flag("data-dir", "Local data directory used for caching purposes (index-header, in-mem cache items and meta.jsons). If removed, no data will be lost, just store will have to rebuild the cache. NOTE: Putting raw blocks here will not cause the store to read them. For such use cases use Prometheus + sidecar. Ignored if --no-cache-index-header option is specified."). 99 Default("./data").StringVar(&sc.dataDir) 100 101 cmd.Flag("cache-index-header", "Cache TSDB index-headers on disk to reduce startup time. When set to true, Thanos Store will download index headers from remote object storage on startup and create a header file on disk. Use --data-dir to set the directory in which index headers will be downloaded."). 102 Default("true").BoolVar(&sc.cacheIndexHeader) 103 104 cmd.Flag("index-cache-size", "Maximum size of items held in the in-memory index cache. Ignored if --index-cache.config or --index-cache.config-file option is specified."). 105 Default("250MB").BytesVar(&sc.indexCacheSizeBytes) 106 107 sc.indexCacheConfigs = *extflag.RegisterPathOrContent(cmd, "index-cache.config", 108 "YAML file that contains index cache configuration. See format details: https://thanos.io/tip/components/store.md/#index-cache", 109 extflag.WithEnvSubstitution(), 110 ) 111 112 sc.cachingBucketConfig = *extflag.RegisterPathOrContent(hidden.HiddenCmdClause(cmd), "store.caching-bucket.config", 113 "YAML that contains configuration for caching bucket. Experimental feature, with high risk of changes. See format details: https://thanos.io/tip/components/store.md/#caching-bucket", 114 extflag.WithEnvSubstitution(), 115 ) 116 117 cmd.Flag("chunk-pool-size", "Maximum size of concurrently allocatable bytes reserved strictly to reuse for chunks in memory."). 118 Default("2GB").BytesVar(&sc.chunkPoolSize) 119 120 cmd.Flag("store.grpc.touched-series-limit", "DEPRECATED: use store.limits.request-series.").Default("0").Uint64Var(&sc.storeRateLimits.SeriesPerRequest) 121 cmd.Flag("store.grpc.series-sample-limit", "DEPRECATED: use store.limits.request-samples.").Default("0").Uint64Var(&sc.storeRateLimits.SamplesPerRequest) 122 123 cmd.Flag("store.grpc.downloaded-bytes-limit", 124 "Maximum amount of downloaded (either fetched or touched) bytes in a single Series/LabelNames/LabelValues call. The Series call fails if this limit is exceeded. 0 means no limit."). 125 Default("0").BytesVar(&sc.maxDownloadedBytes) 126 127 cmd.Flag("store.grpc.series-max-concurrency", "Maximum number of concurrent Series calls.").Default("20").IntVar(&sc.maxConcurrency) 128 129 sc.component = component.Store 130 131 sc.objStoreConfig = *extkingpin.RegisterCommonObjStoreFlags(cmd, "", true) 132 133 cmd.Flag("sync-block-duration", "Repeat interval for syncing the blocks between local and remote view."). 134 Default("3m").DurationVar(&sc.syncInterval) 135 136 cmd.Flag("block-sync-concurrency", "Number of goroutines to use when constructing index-cache.json blocks from object storage. Must be equal or greater than 1."). 137 Default("20").IntVar(&sc.blockSyncConcurrency) 138 139 cmd.Flag("block-meta-fetch-concurrency", "Number of goroutines to use when fetching block metadata from object storage."). 140 Default("32").IntVar(&sc.blockMetaFetchConcurrency) 141 142 cmd.Flag("debug.series-batch-size", "The batch size when fetching series from TSDB blocks. Setting the number too high can lead to slower retrieval, while setting it too low can lead to throttling caused by too many calls made to object storage."). 143 Hidden().Default(strconv.Itoa(store.SeriesBatchSize)).IntVar(&sc.seriesBatchSize) 144 145 cmd.Flag("debug.estimated-max-series-size", "Estimated max series size. Setting a value might result in over fetching data while a small value might result in data refetch. Default value is 64KB."). 146 Hidden().Default(strconv.Itoa(store.EstimatedMaxSeriesSize)).Uint64Var(&sc.estimatedMaxSeriesSize) 147 148 cmd.Flag("debug.estimated-max-chunk-size", "Estimated max chunk size. Setting a value might result in over fetching data while a small value might result in data refetch. Default value is 16KiB."). 149 Hidden().Default(strconv.Itoa(store.EstimatedMaxChunkSize)).Uint64Var(&sc.estimatedMaxChunkSize) 150 151 sc.filterConf = &store.FilterConfig{} 152 153 cmd.Flag("min-time", "Start of time range limit to serve. Thanos Store will serve only metrics, which happened later than this value. Option can be a constant time in RFC3339 format or time duration relative to current time, such as -1d or 2h45m. Valid duration units are ms, s, m, h, d, w, y."). 154 Default("0000-01-01T00:00:00Z").SetValue(&sc.filterConf.MinTime) 155 156 cmd.Flag("max-time", "End of time range limit to serve. Thanos Store will serve only blocks, which happened earlier than this value. Option can be a constant time in RFC3339 format or time duration relative to current time, such as -1d or 2h45m. Valid duration units are ms, s, m, h, d, w, y."). 157 Default("9999-12-31T23:59:59Z").SetValue(&sc.filterConf.MaxTime) 158 159 cmd.Flag("debug.advertise-compatibility-label", "If true, Store Gateway in addition to other labels, will advertise special \"@thanos_compatibility_store_type=store\" label set. This makes store Gateway compatible with Querier before 0.8.0"). 160 Hidden().Default("true").BoolVar(&sc.advertiseCompatibilityLabel) 161 162 sc.selectorRelabelConf = *extkingpin.RegisterSelectorRelabelFlags(cmd) 163 164 cmd.Flag("store.index-header-posting-offsets-in-mem-sampling", "Controls what is the ratio of postings offsets store will hold in memory. "+ 165 "Larger value will keep less offsets, which will increase CPU cycles needed for query touching those postings. It's meant for setups that want low baseline memory pressure and where less traffic is expected. "+ 166 "On the contrary, smaller value will increase baseline memory usage, but improve latency slightly. 1 will keep all in memory. Default value is the same as in Prometheus which gives a good balance."). 167 Hidden().Default(fmt.Sprintf("%v", store.DefaultPostingOffsetInMemorySampling)).IntVar(&sc.postingOffsetsInMemSampling) 168 169 cmd.Flag("consistency-delay", "Minimum age of all blocks before they are being read. Set it to safe value (e.g 30m) if your object storage is eventually consistent. GCS and S3 are (roughly) strongly consistent."). 170 Default("0s").SetValue(&sc.consistencyDelay) 171 172 cmd.Flag("ignore-deletion-marks-delay", "Duration after which the blocks marked for deletion will be filtered out while fetching blocks. "+ 173 "The idea of ignore-deletion-marks-delay is to ignore blocks that are marked for deletion with some delay. This ensures store can still serve blocks that are meant to be deleted but do not have a replacement yet. "+ 174 "If delete-delay duration is provided to compactor or bucket verify component, it will upload deletion-mark.json file to mark after what duration the block should be deleted rather than deleting the block straight away. "+ 175 "If delete-delay is non-zero for compactor or bucket verify component, ignore-deletion-marks-delay should be set to (delete-delay)/2 so that blocks marked for deletion are filtered out while fetching blocks before being deleted from bucket. "+ 176 "Default is 24h, half of the default value for --delete-delay on compactor."). 177 Default("24h").SetValue(&sc.ignoreDeletionMarksDelay) 178 179 cmd.Flag("store.enable-index-header-lazy-reader", "If true, Store Gateway will lazy memory map index-header only once the block is required by a query."). 180 Default("false").BoolVar(&sc.lazyIndexReaderEnabled) 181 182 cmd.Flag("store.index-header-lazy-reader-idle-timeout", "If index-header lazy reader is enabled and this idle timeout setting is > 0, memory map-ed index-headers will be automatically released after 'idle timeout' inactivity."). 183 Hidden().Default("5m").DurationVar(&sc.lazyIndexReaderIdleTimeout) 184 185 cmd.Flag("web.disable", "Disable Block Viewer UI.").Default("false").BoolVar(&sc.disableWeb) 186 187 cmd.Flag("web.external-prefix", "Static prefix for all HTML links and redirect URLs in the bucket web UI interface. Actual endpoints are still served on / or the web.route-prefix. This allows thanos bucket web UI to be served behind a reverse proxy that strips a URL sub-path."). 188 Default("").StringVar(&sc.webConfig.externalPrefix) 189 190 cmd.Flag("web.prefix-header", "Name of HTTP request header used for dynamic prefixing of UI links and redirects. This option is ignored if web.external-prefix argument is set. Security risk: enable this option only if a reverse proxy in front of thanos is resetting the header. The --web.prefix-header=X-Forwarded-Prefix option can be useful, for example, if Thanos UI is served via Traefik reverse proxy with PathPrefixStrip option enabled, which sends the stripped prefix value in X-Forwarded-Prefix header. This allows thanos UI to be served on a sub-path."). 191 Default("").StringVar(&sc.webConfig.prefixHeaderName) 192 193 cmd.Flag("web.disable-cors", "Whether to disable CORS headers to be set by Thanos. By default Thanos sets CORS headers to be allowed by all."). 194 Default("false").BoolVar(&sc.webConfig.disableCORS) 195 196 cmd.Flag("bucket-web-label", "External block label to use as group title in the bucket web UI").StringVar(&sc.label) 197 198 sc.reqLogConfig = extkingpin.RegisterRequestLoggingFlags(cmd) 199 } 200 201 // registerStore registers a store command. 202 func registerStore(app *extkingpin.App) { 203 cmd := app.Command(component.Store.String(), "Store node giving access to blocks in a bucket provider. Now supported GCS, S3, Azure, Swift, Tencent COS and Aliyun OSS.") 204 205 conf := &storeConfig{} 206 conf.registerFlag(cmd) 207 208 cmd.Setup(func(g *run.Group, logger log.Logger, reg *prometheus.Registry, tracer opentracing.Tracer, _ <-chan struct{}, debugLogging bool) error { 209 if conf.filterConf.MinTime.PrometheusTimestamp() > conf.filterConf.MaxTime.PrometheusTimestamp() { 210 return errors.Errorf("invalid argument: --min-time '%s' can't be greater than --max-time '%s'", 211 conf.filterConf.MinTime, conf.filterConf.MaxTime) 212 } 213 214 httpLogOpts, err := logging.ParseHTTPOptions("", conf.reqLogConfig) 215 if err != nil { 216 return errors.Wrap(err, "error while parsing config for request logging") 217 } 218 219 tagOpts, grpcLogOpts, err := logging.ParsegRPCOptions("", conf.reqLogConfig) 220 if err != nil { 221 return errors.Wrap(err, "error while parsing config for request logging") 222 } 223 224 conf.debugLogging = debugLogging 225 226 return runStore(g, 227 logger, 228 reg, 229 tracer, 230 httpLogOpts, 231 grpcLogOpts, 232 tagOpts, 233 *conf, 234 getFlagsMap(cmd.Flags()), 235 ) 236 }) 237 } 238 239 // runStore starts a daemon that serves queries to cluster peers using data from an object store. 240 func runStore( 241 g *run.Group, 242 logger log.Logger, 243 reg *prometheus.Registry, 244 tracer opentracing.Tracer, 245 httpLogOpts []logging.Option, 246 grpcLogOpts []grpclogging.Option, 247 tagOpts []tags.Option, 248 conf storeConfig, 249 flagsMap map[string]string, 250 ) error { 251 dataDir := conf.dataDir 252 if !conf.cacheIndexHeader { 253 dataDir = "" 254 } 255 256 grpcProbe := prober.NewGRPC() 257 httpProbe := prober.NewHTTP() 258 statusProber := prober.Combine( 259 httpProbe, 260 grpcProbe, 261 prober.NewInstrumentation(conf.component, logger, extprom.WrapRegistererWithPrefix("thanos_", reg)), 262 ) 263 264 srv := httpserver.New(logger, reg, conf.component, httpProbe, 265 httpserver.WithListen(conf.httpConfig.bindAddress), 266 httpserver.WithGracePeriod(time.Duration(conf.httpConfig.gracePeriod)), 267 httpserver.WithTLSConfig(conf.httpConfig.tlsConfig), 268 httpserver.WithEnableH2C(true), // For groupcache. 269 ) 270 271 g.Add(func() error { 272 statusProber.Healthy() 273 274 return srv.ListenAndServe() 275 }, func(err error) { 276 statusProber.NotReady(err) 277 defer statusProber.NotHealthy(err) 278 279 srv.Shutdown(err) 280 }) 281 282 confContentYaml, err := conf.objStoreConfig.Content() 283 if err != nil { 284 return err 285 } 286 287 bkt, err := client.NewBucket(logger, confContentYaml, conf.component.String()) 288 if err != nil { 289 return err 290 } 291 insBkt := objstoretracing.WrapWithTraces(objstore.WrapWithMetrics(bkt, extprom.WrapRegistererWithPrefix("thanos_", reg), bkt.Name())) 292 293 cachingBucketConfigYaml, err := conf.cachingBucketConfig.Content() 294 if err != nil { 295 return errors.Wrap(err, "get caching bucket configuration") 296 } 297 298 r := route.New() 299 300 if len(cachingBucketConfigYaml) > 0 { 301 insBkt, err = storecache.NewCachingBucketFromYaml(cachingBucketConfigYaml, insBkt, logger, reg, r) 302 if err != nil { 303 return errors.Wrap(err, "create caching bucket") 304 } 305 } 306 307 relabelContentYaml, err := conf.selectorRelabelConf.Content() 308 if err != nil { 309 return errors.Wrap(err, "get content of relabel configuration") 310 } 311 312 relabelConfig, err := block.ParseRelabelConfig(relabelContentYaml, block.SelectorSupportedRelabelActions) 313 if err != nil { 314 return err 315 } 316 317 indexCacheContentYaml, err := conf.indexCacheConfigs.Content() 318 if err != nil { 319 return errors.Wrap(err, "get content of index cache configuration") 320 } 321 322 // Create the index cache loading its config from config file, while keeping 323 // backward compatibility with the pre-config file era. 324 var indexCache storecache.IndexCache 325 if len(indexCacheContentYaml) > 0 { 326 indexCache, err = storecache.NewIndexCache(logger, indexCacheContentYaml, reg) 327 } else { 328 indexCache, err = storecache.NewInMemoryIndexCacheWithConfig(logger, nil, reg, storecache.InMemoryIndexCacheConfig{ 329 MaxSize: model.Bytes(conf.indexCacheSizeBytes), 330 MaxItemSize: storecache.DefaultInMemoryIndexCacheConfig.MaxItemSize, 331 }) 332 } 333 if err != nil { 334 return errors.Wrap(err, "create index cache") 335 } 336 337 ignoreDeletionMarkFilter := block.NewIgnoreDeletionMarkFilter(logger, insBkt, time.Duration(conf.ignoreDeletionMarksDelay), conf.blockMetaFetchConcurrency) 338 metaFetcher, err := block.NewMetaFetcher(logger, conf.blockMetaFetchConcurrency, insBkt, dataDir, extprom.WrapRegistererWithPrefix("thanos_", reg), 339 []block.MetadataFilter{ 340 block.NewTimePartitionMetaFilter(conf.filterConf.MinTime, conf.filterConf.MaxTime), 341 block.NewLabelShardedMetaFilter(relabelConfig), 342 block.NewConsistencyDelayMetaFilter(logger, time.Duration(conf.consistencyDelay), extprom.WrapRegistererWithPrefix("thanos_", reg)), 343 ignoreDeletionMarkFilter, 344 block.NewDeduplicateFilter(conf.blockMetaFetchConcurrency), 345 }) 346 if err != nil { 347 return errors.Wrap(err, "meta fetcher") 348 } 349 350 // Limit the concurrency on queries against the Thanos store. 351 if conf.maxConcurrency < 0 { 352 return errors.Errorf("max concurrency value cannot be lower than 0 (got %v)", conf.maxConcurrency) 353 } 354 355 queriesGate := gate.New(extprom.WrapRegistererWithPrefix("thanos_bucket_store_series_", reg), int(conf.maxConcurrency), gate.Queries) 356 357 chunkPool, err := store.NewDefaultChunkBytesPool(uint64(conf.chunkPoolSize)) 358 if err != nil { 359 return errors.Wrap(err, "create chunk pool") 360 } 361 362 options := []store.BucketStoreOption{ 363 store.WithLogger(logger), 364 store.WithRegistry(reg), 365 store.WithIndexCache(indexCache), 366 store.WithQueryGate(queriesGate), 367 store.WithChunkPool(chunkPool), 368 store.WithFilterConfig(conf.filterConf), 369 store.WithChunkHashCalculation(true), 370 store.WithSeriesBatchSize(conf.seriesBatchSize), 371 store.WithBlockEstimatedMaxSeriesFunc(func(m metadata.Meta) uint64 { 372 if m.Thanos.IndexStats.SeriesMaxSize > 0 && 373 uint64(m.Thanos.IndexStats.SeriesMaxSize) < conf.estimatedMaxSeriesSize { 374 return uint64(m.Thanos.IndexStats.SeriesMaxSize) 375 } 376 return conf.estimatedMaxSeriesSize 377 }), 378 store.WithBlockEstimatedMaxChunkFunc(func(m metadata.Meta) uint64 { 379 if m.Thanos.IndexStats.ChunkMaxSize > 0 && 380 uint64(m.Thanos.IndexStats.ChunkMaxSize) < conf.estimatedMaxChunkSize { 381 return uint64(m.Thanos.IndexStats.ChunkMaxSize) 382 } 383 return conf.estimatedMaxChunkSize 384 }), 385 } 386 387 if conf.debugLogging { 388 options = append(options, store.WithDebugLogging()) 389 } 390 391 bs, err := store.NewBucketStore( 392 insBkt, 393 metaFetcher, 394 dataDir, 395 store.NewChunksLimiterFactory(conf.storeRateLimits.SamplesPerRequest/store.MaxSamplesPerChunk), // The samples limit is an approximation based on the max number of samples per chunk. 396 store.NewSeriesLimiterFactory(conf.storeRateLimits.SeriesPerRequest), 397 store.NewBytesLimiterFactory(conf.maxDownloadedBytes), 398 store.NewGapBasedPartitioner(store.PartitionerMaxGapSize), 399 conf.blockSyncConcurrency, 400 conf.advertiseCompatibilityLabel, 401 conf.postingOffsetsInMemSampling, 402 false, 403 conf.lazyIndexReaderEnabled, 404 conf.lazyIndexReaderIdleTimeout, 405 options..., 406 ) 407 if err != nil { 408 return errors.Wrap(err, "create object storage store") 409 } 410 411 // bucketStoreReady signals when bucket store is ready. 412 bucketStoreReady := make(chan struct{}) 413 { 414 ctx, cancel := context.WithCancel(context.Background()) 415 g.Add(func() error { 416 defer runutil.CloseWithLogOnErr(logger, insBkt, "bucket client") 417 418 level.Info(logger).Log("msg", "initializing bucket store") 419 begin := time.Now() 420 421 // This will stop retrying after set timeout duration. 422 initialSyncCtx, cancel := context.WithTimeout(ctx, retryTimeoutDuration*time.Second) 423 defer cancel() 424 425 // Retry in case of error. 426 err := runutil.Retry(retryIntervalDuration*time.Second, initialSyncCtx.Done(), func() error { 427 return bs.InitialSync(ctx) 428 }) 429 430 if err != nil { 431 close(bucketStoreReady) 432 return errors.Wrap(err, "bucket store initial sync") 433 } 434 435 level.Info(logger).Log("msg", "bucket store ready", "init_duration", time.Since(begin).String()) 436 close(bucketStoreReady) 437 438 err = runutil.Repeat(conf.syncInterval, ctx.Done(), func() error { 439 if err := bs.SyncBlocks(ctx); err != nil { 440 level.Warn(logger).Log("msg", "syncing blocks failed", "err", err) 441 } 442 return nil 443 }) 444 445 runutil.CloseWithLogOnErr(logger, bs, "bucket store") 446 return err 447 }, func(error) { 448 cancel() 449 }) 450 } 451 452 infoSrv := info.NewInfoServer( 453 component.Store.String(), 454 info.WithLabelSetFunc(func() []labelpb.ZLabelSet { 455 return bs.LabelSet() 456 }), 457 info.WithStoreInfoFunc(func() *infopb.StoreInfo { 458 if httpProbe.IsReady() { 459 mint, maxt := bs.TimeRange() 460 return &infopb.StoreInfo{ 461 MinTime: mint, 462 MaxTime: maxt, 463 SupportsSharding: true, 464 SupportsWithoutReplicaLabels: true, 465 TsdbInfos: bs.TSDBInfos(), 466 } 467 } 468 return nil 469 }), 470 ) 471 472 // Start query (proxy) gRPC StoreAPI. 473 { 474 tlsCfg, err := tls.NewServerConfig(log.With(logger, "protocol", "gRPC"), conf.grpcConfig.tlsSrvCert, conf.grpcConfig.tlsSrvKey, conf.grpcConfig.tlsSrvClientCA) 475 if err != nil { 476 return errors.Wrap(err, "setup gRPC server") 477 } 478 479 storeServer := store.NewInstrumentedStoreServer(reg, bs) 480 s := grpcserver.New(logger, reg, tracer, grpcLogOpts, tagOpts, conf.component, grpcProbe, 481 grpcserver.WithServer(store.RegisterStoreServer(storeServer, logger)), 482 grpcserver.WithServer(info.RegisterInfoServer(infoSrv)), 483 grpcserver.WithListen(conf.grpcConfig.bindAddress), 484 grpcserver.WithGracePeriod(conf.grpcConfig.gracePeriod), 485 grpcserver.WithMaxConnAge(conf.grpcConfig.maxConnectionAge), 486 grpcserver.WithTLSConfig(tlsCfg), 487 ) 488 489 g.Add(func() error { 490 <-bucketStoreReady 491 statusProber.Ready() 492 return s.ListenAndServe() 493 }, func(err error) { 494 statusProber.NotReady(err) 495 s.Shutdown(err) 496 }) 497 } 498 499 // Add bucket UI for loaded blocks. 500 { 501 ins := extpromhttp.NewInstrumentationMiddleware(reg, nil) 502 503 if !conf.disableWeb { 504 compactorView := ui.NewBucketUI(logger, conf.webConfig.externalPrefix, conf.webConfig.prefixHeaderName, conf.component) 505 compactorView.Register(r, ins) 506 507 // Configure Request Logging for HTTP calls. 508 logMiddleware := logging.NewHTTPServerMiddleware(logger, httpLogOpts...) 509 api := blocksAPI.NewBlocksAPI(logger, conf.webConfig.disableCORS, conf.label, flagsMap, insBkt) 510 api.Register(r.WithPrefix("/api/v1"), tracer, logger, ins, logMiddleware) 511 512 metaFetcher.UpdateOnChange(func(blocks []metadata.Meta, err error) { 513 api.SetLoaded(blocks, err) 514 }) 515 } 516 517 srv.Handle("/", r) 518 } 519 520 level.Info(logger).Log("msg", "starting store node") 521 return nil 522 }