|
|
|
@@ -121,6 +121,9 @@ type searchSupport struct {
|
|
|
|
|
|
|
|
|
|
// testing
|
|
|
|
|
clientIndexEventsChan chan *IndexEvent
|
|
|
|
|
|
|
|
|
|
// periodic rebuilding of the indexes to keep usage insights up to date
|
|
|
|
|
rebuildInterval time.Duration
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
@@ -153,6 +156,7 @@ func newSearchSupport(opts SearchOptions, storage StorageBackend, access types.A
|
|
|
|
|
clientIndexEventsChan: opts.IndexEventsChan,
|
|
|
|
|
indexEventsChan: make(chan *IndexEvent),
|
|
|
|
|
indexQueueProcessors: make(map[string]*indexQueueProcessor),
|
|
|
|
|
rebuildInterval: opts.RebuildInterval,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info, err := opts.Resources.GetDocumentBuilders()
|
|
|
|
@@ -377,34 +381,52 @@ func (s *searchSupport) GetStats(ctx context.Context, req *resourcepb.ResourceSt
|
|
|
|
|
return rsp, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// init is called during startup. any failure will block startup and continued execution
|
|
|
|
|
func (s *searchSupport) init(ctx context.Context) error {
|
|
|
|
|
ctx, span := s.tracer.Start(ctx, tracingPrexfixSearch+"Init")
|
|
|
|
|
defer span.End()
|
|
|
|
|
start := time.Now().Unix()
|
|
|
|
|
|
|
|
|
|
func (s *searchSupport) buildIndexes(ctx context.Context, rebuild bool) (int, error) {
|
|
|
|
|
totalBatchesIndexed := 0
|
|
|
|
|
group := errgroup.Group{}
|
|
|
|
|
group.SetLimit(s.initWorkers)
|
|
|
|
|
|
|
|
|
|
stats, err := s.storage.GetResourceStats(ctx, "", s.initMinSize)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, info := range stats {
|
|
|
|
|
// only periodically rebuild the dashboard index, specifically to update the usage insights data
|
|
|
|
|
if rebuild && info.Resource != dashboardv1.DASHBOARD_RESOURCE {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
group.Go(func() error {
|
|
|
|
|
s.log.Debug("initializing search index", "namespace", info.Namespace, "group", info.Group, "resource", info.Resource)
|
|
|
|
|
if rebuild {
|
|
|
|
|
// we need to clear the cache to make sure we get the latest usage insights data
|
|
|
|
|
s.builders.clearNamespacedCache(info.NamespacedResource)
|
|
|
|
|
}
|
|
|
|
|
s.log.Debug("building index", "namespace", info.Namespace, "group", info.Group, "resource", info.Resource)
|
|
|
|
|
totalBatchesIndexed++
|
|
|
|
|
_, _, err = s.build(ctx, info.NamespacedResource, info.Count, info.ResourceVersion)
|
|
|
|
|
_, _, err := s.build(ctx, info.NamespacedResource, info.Count, info.ResourceVersion)
|
|
|
|
|
return err
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = group.Wait()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return totalBatchesIndexed, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return totalBatchesIndexed, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *searchSupport) init(ctx context.Context) error {
|
|
|
|
|
ctx, span := s.tracer.Start(ctx, tracingPrexfixSearch+"Init")
|
|
|
|
|
defer span.End()
|
|
|
|
|
start := time.Now().Unix()
|
|
|
|
|
|
|
|
|
|
totalBatchesIndexed, err := s.buildIndexes(ctx, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
span.AddEvent("namespaces indexed", trace.WithAttributes(attribute.Int("namespaced_indexed", totalBatchesIndexed)))
|
|
|
|
|
|
|
|
|
|
// Now start listening for new events
|
|
|
|
@@ -428,6 +450,12 @@ func (s *searchSupport) init(ctx context.Context) error {
|
|
|
|
|
|
|
|
|
|
go s.monitorIndexEvents(ctx)
|
|
|
|
|
|
|
|
|
|
// since usage insights is not in unified storage, we need to periodically rebuild the index
|
|
|
|
|
// to make sure these data points are up to date.
|
|
|
|
|
if s.rebuildInterval > 0 {
|
|
|
|
|
go s.startPeriodicRebuild(watchctx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end := time.Now().Unix()
|
|
|
|
|
s.log.Info("search index initialized", "duration_secs", end-start, "total_docs", s.search.TotalDocs())
|
|
|
|
|
if s.indexMetrics != nil {
|
|
|
|
@@ -508,6 +536,54 @@ func (s *searchSupport) monitorIndexEvents(ctx context.Context) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *searchSupport) startPeriodicRebuild(ctx context.Context) {
|
|
|
|
|
ticker := time.NewTicker(s.rebuildInterval)
|
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
|
|
s.log.Info("starting periodic index rebuild", "interval", s.rebuildInterval)
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
s.log.Info("stopping periodic index rebuild due to context cancellation")
|
|
|
|
|
return
|
|
|
|
|
case <-ticker.C:
|
|
|
|
|
s.log.Info("starting periodic index rebuild")
|
|
|
|
|
if err := s.rebuildDashboardIndexes(ctx); err != nil {
|
|
|
|
|
s.log.Error("error during periodic index rebuild", "error", err)
|
|
|
|
|
} else {
|
|
|
|
|
s.log.Info("periodic index rebuild completed successfully")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *searchSupport) rebuildDashboardIndexes(ctx context.Context) error {
|
|
|
|
|
ctx, span := s.tracer.Start(ctx, tracingPrexfixSearch+"RebuildDashboardIndexes")
|
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
|
|
start := time.Now()
|
|
|
|
|
s.log.Info("rebuilding all search indexes")
|
|
|
|
|
|
|
|
|
|
totalBatchesIndexed, err := s.buildIndexes(ctx, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("failed to rebuild dashboard indexes: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end := time.Now()
|
|
|
|
|
duration := end.Sub(start)
|
|
|
|
|
s.log.Info("completed rebuilding all dashboard search indexes",
|
|
|
|
|
"duration", duration,
|
|
|
|
|
"rebuilt_indexes", totalBatchesIndexed,
|
|
|
|
|
"total_docs", s.search.TotalDocs())
|
|
|
|
|
|
|
|
|
|
if s.indexMetrics != nil {
|
|
|
|
|
s.indexMetrics.IndexCreationTime.WithLabelValues().Observe(duration.Seconds())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *searchSupport) getOrCreateIndex(ctx context.Context, key NamespacedResource) (ResourceIndex, error) {
|
|
|
|
|
if s == nil || s.search == nil {
|
|
|
|
|
return nil, fmt.Errorf("search is not configured properly (missing unifiedStorageSearch feature toggle?)")
|
|
|
|
@@ -770,3 +846,9 @@ func (s *searchSupport) getOrCreateIndexQueueProcessor(index ResourceIndex, nsr
|
|
|
|
|
s.indexQueueProcessors[key] = indexQueueProcessor
|
|
|
|
|
return indexQueueProcessor, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *builderCache) clearNamespacedCache(key NamespacedResource) {
|
|
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
s.ns.Remove(key)
|
|
|
|
|
}
|
|
|
|
|