github.com/weaviate/weaviate@v1.24.6/adapters/repos/db/nodes.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package db
    13  
    14  import (
    15  	"context"
    16  	"fmt"
    17  	"sort"
    18  
    19  	"github.com/pkg/errors"
    20  	enterrors "github.com/weaviate/weaviate/entities/errors"
    21  	"github.com/weaviate/weaviate/entities/models"
    22  	"github.com/weaviate/weaviate/entities/schema"
    23  	"github.com/weaviate/weaviate/entities/verbosity"
    24  )
    25  
    26  // GetNodeStatus returns the status of all Weaviate nodes.
    27  func (db *DB) GetNodeStatus(ctx context.Context, className string, verbosity string) ([]*models.NodeStatus, error) {
    28  	nodeStatuses := make([]*models.NodeStatus, len(db.schemaGetter.Nodes()))
    29  	eg := enterrors.NewErrorGroupWrapper(db.logger)
    30  	eg.SetLimit(_NUMCPU)
    31  	for i, nodeName := range db.schemaGetter.Nodes() {
    32  		i, nodeName := i, nodeName
    33  		eg.Go(func() error {
    34  			status, err := db.getNodeStatus(ctx, nodeName, className, verbosity)
    35  			if err != nil {
    36  				return fmt.Errorf("node: %v: %w", nodeName, err)
    37  			}
    38  			if status.Status == nil {
    39  				return enterrors.NewErrNotFound(
    40  					fmt.Errorf("class %q not found", className))
    41  			}
    42  			nodeStatuses[i] = status
    43  
    44  			return nil
    45  		}, nodeName)
    46  	}
    47  
    48  	if err := eg.Wait(); err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	sort.Slice(nodeStatuses, func(i, j int) bool {
    53  		return nodeStatuses[i].Name < nodeStatuses[j].Name
    54  	})
    55  	return nodeStatuses, nil
    56  }
    57  
    58  func (db *DB) getNodeStatus(ctx context.Context, nodeName string, className, output string) (*models.NodeStatus, error) {
    59  	if db.schemaGetter.NodeName() == nodeName {
    60  		return db.LocalNodeStatus(ctx, className, output), nil
    61  	}
    62  	status, err := db.remoteNode.GetNodeStatus(ctx, nodeName, className, output)
    63  	if err != nil {
    64  		switch typed := err.(type) {
    65  		case enterrors.ErrSendHttpRequest:
    66  			if errors.Is(typed.Unwrap(), context.DeadlineExceeded) {
    67  				nodeTimeout := models.NodeStatusStatusTIMEOUT
    68  				return &models.NodeStatus{Name: nodeName, Status: &nodeTimeout}, nil
    69  			}
    70  
    71  			nodeUnavailable := models.NodeStatusStatusUNAVAILABLE
    72  			return &models.NodeStatus{Name: nodeName, Status: &nodeUnavailable}, nil
    73  		case enterrors.ErrOpenHttpRequest:
    74  			nodeUnavailable := models.NodeStatusStatusUNAVAILABLE
    75  			return &models.NodeStatus{Name: nodeName, Status: &nodeUnavailable}, nil
    76  		default:
    77  			return nil, err
    78  		}
    79  	}
    80  	return status, nil
    81  }
    82  
    83  // IncomingGetNodeStatus returns the index if it exists or nil if it doesn't
    84  func (db *DB) IncomingGetNodeStatus(ctx context.Context, className, verbosity string) (*models.NodeStatus, error) {
    85  	return db.LocalNodeStatus(ctx, className, verbosity), nil
    86  }
    87  
    88  func (db *DB) LocalNodeStatus(ctx context.Context, className, output string) *models.NodeStatus {
    89  	if className != "" && db.GetIndex(schema.ClassName(className)) == nil {
    90  		// class not found
    91  		return &models.NodeStatus{}
    92  	}
    93  
    94  	var (
    95  		shards    []*models.NodeShardStatus
    96  		nodeStats *models.NodeStats
    97  	)
    98  	if output == verbosity.OutputVerbose {
    99  		nodeStats = db.localNodeShardStats(ctx, &shards, className)
   100  	}
   101  
   102  	clusterHealthStatus := models.NodeStatusStatusHEALTHY
   103  	if db.schemaGetter.ClusterHealthScore() > 0 {
   104  		clusterHealthStatus = models.NodeStatusStatusUNHEALTHY
   105  	}
   106  
   107  	status := models.NodeStatus{
   108  		Name:       db.schemaGetter.NodeName(),
   109  		Version:    db.config.ServerVersion,
   110  		GitHash:    db.config.GitHash,
   111  		Status:     &clusterHealthStatus,
   112  		Shards:     shards,
   113  		Stats:      nodeStats,
   114  		BatchStats: db.localNodeBatchStats(),
   115  	}
   116  
   117  	return &status
   118  }
   119  
   120  func (db *DB) localNodeShardStats(ctx context.Context,
   121  	status *[]*models.NodeShardStatus, className string,
   122  ) *models.NodeStats {
   123  	var objectCount, shardCount int64
   124  	if className == "" {
   125  		db.indexLock.RLock()
   126  		defer db.indexLock.RUnlock()
   127  		for name, idx := range db.indices {
   128  			if idx == nil {
   129  				db.logger.WithField("action", "local_node_status_for_all").
   130  					Warningf("no resource found for index %q", name)
   131  				continue
   132  			}
   133  			objects, shards := idx.getShardsNodeStatus(ctx, status)
   134  			objectCount, shardCount = objectCount+objects, shardCount+shards
   135  		}
   136  		return &models.NodeStats{
   137  			ObjectCount: objectCount,
   138  			ShardCount:  shardCount,
   139  		}
   140  	}
   141  	idx := db.GetIndex(schema.ClassName(className))
   142  	if idx == nil {
   143  		db.logger.WithField("action", "local_node_status_for_class").
   144  			Warningf("no index found for class %q", className)
   145  		return nil
   146  	}
   147  	objectCount, shardCount = idx.getShardsNodeStatus(ctx, status)
   148  	return &models.NodeStats{
   149  		ObjectCount: objectCount,
   150  		ShardCount:  shardCount,
   151  	}
   152  }
   153  
   154  func (db *DB) localNodeBatchStats() *models.BatchStats {
   155  	db.batchMonitorLock.Lock()
   156  	rate := db.ratePerSecond
   157  	db.batchMonitorLock.Unlock()
   158  	stats := &models.BatchStats{RatePerSecond: int64(rate)}
   159  	if !asyncEnabled() {
   160  		ql := int64(len(db.jobQueueCh))
   161  		stats.QueueLength = &ql
   162  	}
   163  	return stats
   164  }
   165  
   166  func (i *Index) getShardsNodeStatus(ctx context.Context,
   167  	status *[]*models.NodeShardStatus,
   168  ) (totalCount, shardCount int64) {
   169  	i.ForEachShard(func(name string, shard ShardLike) error {
   170  		if err := ctx.Err(); err != nil {
   171  			return err
   172  		}
   173  
   174  		// Don't force load a lazy shard to get nodes status
   175  		if lazy, ok := shard.(*LazyLoadShard); ok {
   176  			if !lazy.isLoaded() {
   177  				shardStatus := &models.NodeShardStatus{
   178  					Name:                 name,
   179  					Class:                shard.Index().Config.ClassName.String(),
   180  					VectorIndexingStatus: shard.GetStatus().String(),
   181  					Loaded:               false,
   182  				}
   183  				*status = append(*status, shardStatus)
   184  				shardCount++
   185  				return nil
   186  			}
   187  		}
   188  
   189  		objectCount := int64(shard.ObjectCountAsync())
   190  		totalCount += objectCount
   191  
   192  		// FIXME stats of target vectors
   193  		var queueLen int64
   194  		var compressed bool
   195  		if shard.hasTargetVectors() {
   196  			for _, queue := range shard.Queues() {
   197  				queueLen += queue.Size()
   198  			}
   199  			for _, vectorIndex := range shard.VectorIndexes() {
   200  				if vectorIndex.Compressed() {
   201  					compressed = true
   202  					break
   203  				}
   204  			}
   205  		} else {
   206  			queueLen = shard.Queue().Size()
   207  			compressed = shard.VectorIndex().Compressed()
   208  		}
   209  
   210  		shardStatus := &models.NodeShardStatus{
   211  			Name:                 name,
   212  			Class:                shard.Index().Config.ClassName.String(),
   213  			ObjectCount:          objectCount,
   214  			VectorIndexingStatus: shard.GetStatus().String(),
   215  			VectorQueueLength:    queueLen,
   216  			Compressed:           compressed,
   217  			Loaded:               true,
   218  		}
   219  		*status = append(*status, shardStatus)
   220  		shardCount++
   221  		return nil
   222  	})
   223  	return
   224  }