github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/tsdb/store.go (about)

     1  package tsdb
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/go-kit/log/level"
     8  	"github.com/prometheus/client_golang/prometheus"
     9  	"github.com/prometheus/prometheus/model/labels"
    10  
    11  	"github.com/grafana/loki/pkg/storage/chunk/client"
    12  	"github.com/grafana/loki/pkg/storage/chunk/fetcher"
    13  	"github.com/grafana/loki/pkg/storage/config"
    14  	"github.com/grafana/loki/pkg/storage/stores"
    15  	"github.com/grafana/loki/pkg/storage/stores/indexshipper"
    16  	"github.com/grafana/loki/pkg/storage/stores/indexshipper/downloads"
    17  	"github.com/grafana/loki/pkg/storage/stores/series"
    18  	"github.com/grafana/loki/pkg/storage/stores/tsdb/index"
    19  	util_log "github.com/grafana/loki/pkg/util/log"
    20  )
    21  
    22  type store struct {
    23  	indexShipper indexshipper.IndexShipper
    24  	indexWriter  IndexWriter
    25  	indexStore   series.IndexStore
    26  	stopOnce     sync.Once
    27  }
    28  
    29  type newStoreFactoryFunc func(
    30  	indexShipperCfg indexshipper.Config,
    31  	p config.PeriodConfig,
    32  	f *fetcher.Fetcher,
    33  	objectClient client.ObjectClient,
    34  	limits downloads.Limits,
    35  	tableRanges config.TableRanges,
    36  	reg prometheus.Registerer,
    37  ) (
    38  	chunkWriter stores.ChunkWriter,
    39  	indexStore series.IndexStore,
    40  	stopFunc func(),
    41  	err error,
    42  )
    43  
    44  // NewStore creates a new store if not initialized already.
    45  // Each call to NewStore will always build a new stores.ChunkWriter even if the store was already initialized since
    46  // fetcher.Fetcher instances could be different due to periodic configs having different types of object storage configured
    47  // for storing chunks.
    48  // It also helps us make tsdb store a singleton because
    49  // we do not need to build store for each schema config since we do not do any schema specific handling yet.
    50  // If we do need to do schema specific handling, it would be a good idea to abstract away the handling since
    51  // running multiple head managers would be complicated and wasteful.
    52  var NewStore = func() newStoreFactoryFunc {
    53  	var storeInstance *store
    54  	return func(
    55  		indexShipperCfg indexshipper.Config,
    56  		p config.PeriodConfig,
    57  		f *fetcher.Fetcher,
    58  		objectClient client.ObjectClient,
    59  		limits downloads.Limits,
    60  		tableRanges config.TableRanges,
    61  		reg prometheus.Registerer,
    62  	) (
    63  		stores.ChunkWriter,
    64  		series.IndexStore,
    65  		func(),
    66  		error,
    67  	) {
    68  		if storeInstance == nil {
    69  			storeInstance = &store{}
    70  			err := storeInstance.init(indexShipperCfg, objectClient, limits, tableRanges, reg)
    71  			if err != nil {
    72  				return nil, nil, nil, err
    73  			}
    74  		}
    75  
    76  		return NewChunkWriter(f, p, storeInstance.indexWriter), storeInstance.indexStore, storeInstance.Stop, nil
    77  	}
    78  }()
    79  
    80  func (s *store) init(indexShipperCfg indexshipper.Config, objectClient client.ObjectClient,
    81  	limits downloads.Limits, tableRanges config.TableRanges, reg prometheus.Registerer) error {
    82  
    83  	var err error
    84  	s.indexShipper, err = indexshipper.NewIndexShipper(
    85  		indexShipperCfg,
    86  		objectClient,
    87  		limits,
    88  		nil,
    89  		OpenShippableTSDB,
    90  		tableRanges,
    91  		prometheus.WrapRegistererWithPrefix("loki_tsdb_shipper_", reg),
    92  	)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	var indices []Index
    98  
    99  	if indexShipperCfg.Mode != indexshipper.ModeReadOnly {
   100  		var (
   101  			nodeName = indexShipperCfg.IngesterName
   102  			dir      = indexShipperCfg.ActiveIndexDirectory
   103  		)
   104  
   105  		tsdbMetrics := NewMetrics(reg)
   106  		tsdbManager := NewTSDBManager(
   107  			nodeName,
   108  			dir,
   109  			s.indexShipper,
   110  			tableRanges,
   111  			util_log.Logger,
   112  			tsdbMetrics,
   113  		)
   114  
   115  		headManager := NewHeadManager(
   116  			util_log.Logger,
   117  			dir,
   118  			tsdbMetrics,
   119  			tsdbManager,
   120  		)
   121  		if err := headManager.Start(); err != nil {
   122  			return err
   123  		}
   124  
   125  		s.indexWriter = headManager
   126  		indices = append(indices, headManager)
   127  	} else {
   128  		s.indexWriter = failingIndexWriter{}
   129  	}
   130  
   131  	indices = append(indices, newIndexShipperQuerier(s.indexShipper, tableRanges))
   132  	multiIndex, err := NewMultiIndex(indices...)
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	s.indexStore = NewIndexClient(multiIndex)
   138  
   139  	return nil
   140  }
   141  
   142  func (s *store) Stop() {
   143  	s.stopOnce.Do(func() {
   144  		if hm, ok := s.indexWriter.(*HeadManager); ok {
   145  			if err := hm.Stop(); err != nil {
   146  				level.Error(util_log.Logger).Log("msg", "failed to stop head manager", "err", err)
   147  			}
   148  		}
   149  		s.indexShipper.Stop()
   150  	})
   151  }
   152  
   153  type failingIndexWriter struct{}
   154  
   155  func (f failingIndexWriter) Append(_ string, _ labels.Labels, _ index.ChunkMetas) error {
   156  	return fmt.Errorf("index writer is not initialized due to tsdb store being initialized in read-only mode")
   157  }