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 }