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  }