mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-29 00:10:42 +01:00
* vector: weed-vector registers with the weed master weed-vector-server --masters holds a KeepConnected stream to the weed master, the same mechanism filer and s3 use via wdclient — the server shows up in ListClusterNodes(client_type=vector). Resends registration and rediscovers on failure. * vector: s3 discovers weed-vector through the master With -vectors.grpc unset the vectors gateway resolves one registered weed-vector via ListClusterNodes on the masters it already derives from filer config — discovery replaces the static dial for the single-worker OSS path. The conformance harness registers the engine to mini's master and drops -vectors.grpc. * vector: default-fill registration fields ..Default::default() keeps the source compiling against proto variants that carry extra fields, so the same file works in either tree. * vector: serve the Vectors API on the main s3 port The vectors router sits on the main -port listener behind a dispatch that checks the SigV4 credential scope for /s3vectors/ — no separate port needed. -port.vectors stays for explicit dedicated-port deployments; the conformance harness now hits vectors through :8334. * vector: drop -port.vectors entirely It was new — no compat to preserve. The main -port listener serves the API whenever a weed-vector backend is reachable. * vector: drop -vectors.grpc; discovery is the only locator weed-vector always registers to master, the gateway always resolves it through ListClusterNodes — a static address just forks membership. * vector: binary is weed-vector Matches the enterprise binary; package and lib names are unchanged. * vector: review fixes — cluster type, grpc port, lazy resolve, leader hint, advertise - cluster: register 'vector' as a node type so ListClusterNodes returns weed-vector members - s3: dial masters via ToGrpcAddress (HTTP port + 10000) - s3: backend resolves lazily through vectorDiscovery — workers joining after gateway startup work on first query, and address changes re-dial - master_client: convert master HTTP addr to grpc +10000, honor http(s) scheme, follow the leader hint in KeepConnectedResponse, and --advertise covers wildcard/unreachable listen addresses * vector: no --advertise flag; derive the outbound address A wildcard listen resolves to the source IP the master sees via a route lookup, so registration always advertises something reachable without another flag. * vector: review fixes — plaintext dispatch, conn leaks, discovery backoff The two plaintext http servers now serve the vectors dispatch too — signed s3vectors calls on the main port reach the right router. discoverVectorWorker closes each master conn and bounds lookups to 5s; a dead or absent worker no longer re-hits master on every RPC — the resolver backs off for 5s between rediscovery attempts. * vector: 1 GiB grpc cap, dead-worker invalidation, CLI rename Valid PutVectors batches exceed the tonic 4 MiB default — match the gateway's 1 GiB cap. A replaced worker no longer sticks: any RPC error resets the cached address and the next call rediscovers through the master. The core CLI bin renames to weed-vector-cli so the two crates do not collide on the same output path. * vector: retry-only-transport reset, periodic re-resolve, cli rename - reset() takes the failed client and runs only on transport errors — an InvalidArgument on a healthy conn no longer interrupts unrelated requests, and a concurrent re-resolve wins the race. - cli() re-resolves once the interval passes even with a live client, so a worker that moves addresses is picked up. - verify.rs spike docs use weed-vector-cli after the bin rename. * vector: only Unavailable counts as a transport failure A request deadline or an application error can fire on a healthy connection — resetting the shared conn on those churned reconnects.
205 lines
6.2 KiB
Go
205 lines
6.2 KiB
Go
package cluster
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
|
|
)
|
|
|
|
const (
|
|
MasterType = "master"
|
|
VolumeServerType = "volumeServer"
|
|
FilerType = "filer"
|
|
BrokerType = "broker"
|
|
S3Type = "s3"
|
|
VectorType = "vector"
|
|
)
|
|
|
|
type FilerGroupName string
|
|
type DataCenter string
|
|
type Rack string
|
|
|
|
type ClusterNode struct {
|
|
Address pb.ServerAddress
|
|
Version string
|
|
counter int
|
|
CreatedTs time.Time
|
|
DataCenter DataCenter
|
|
Rack Rack
|
|
}
|
|
|
|
type ClusterNodeGroups struct {
|
|
groupMembers map[FilerGroupName]*GroupMembers
|
|
sync.RWMutex
|
|
}
|
|
type Cluster struct {
|
|
filerGroups *ClusterNodeGroups
|
|
brokerGroups *ClusterNodeGroups
|
|
s3Groups *ClusterNodeGroups
|
|
vectorGroups *ClusterNodeGroups
|
|
}
|
|
|
|
func newClusterNodeGroups() *ClusterNodeGroups {
|
|
return &ClusterNodeGroups{
|
|
groupMembers: map[FilerGroupName]*GroupMembers{},
|
|
}
|
|
}
|
|
func (g *ClusterNodeGroups) getGroupMembers(filerGroup FilerGroupName, createIfNotFound bool) *GroupMembers {
|
|
members, found := g.groupMembers[filerGroup]
|
|
if !found && createIfNotFound {
|
|
members = newGroupMembers()
|
|
g.groupMembers[filerGroup] = members
|
|
}
|
|
return members
|
|
}
|
|
|
|
func (g *ClusterNodeGroups) AddClusterNode(filerGroup FilerGroupName, nodeType string, dataCenter DataCenter, rack Rack, address pb.ServerAddress, version string) []*master_pb.KeepConnectedResponse {
|
|
g.Lock()
|
|
defer g.Unlock()
|
|
m := g.getGroupMembers(filerGroup, true)
|
|
if t := m.addMember(dataCenter, rack, address, version); t != nil {
|
|
return buildClusterNodeUpdateMessage(true, filerGroup, nodeType, address)
|
|
}
|
|
return nil
|
|
}
|
|
func (g *ClusterNodeGroups) RemoveClusterNode(filerGroup FilerGroupName, nodeType string, address pb.ServerAddress) []*master_pb.KeepConnectedResponse {
|
|
g.Lock()
|
|
defer g.Unlock()
|
|
m := g.getGroupMembers(filerGroup, false)
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
if m.removeMember(address) {
|
|
return buildClusterNodeUpdateMessage(false, filerGroup, nodeType, address)
|
|
}
|
|
return nil
|
|
}
|
|
func (g *ClusterNodeGroups) ListClusterNode(filerGroup FilerGroupName) (nodes []*ClusterNode) {
|
|
g.Lock()
|
|
defer g.Unlock()
|
|
m := g.getGroupMembers(filerGroup, false)
|
|
if m == nil {
|
|
return nil
|
|
}
|
|
for _, node := range m.members {
|
|
nodes = append(nodes, node)
|
|
}
|
|
return
|
|
}
|
|
|
|
func NewCluster() *Cluster {
|
|
return &Cluster{
|
|
filerGroups: newClusterNodeGroups(),
|
|
brokerGroups: newClusterNodeGroups(),
|
|
s3Groups: newClusterNodeGroups(),
|
|
vectorGroups: newClusterNodeGroups(),
|
|
}
|
|
}
|
|
|
|
func (cluster *Cluster) AddClusterNode(ns, nodeType string, dataCenter DataCenter, rack Rack, address pb.ServerAddress, version string) []*master_pb.KeepConnectedResponse {
|
|
filerGroup := FilerGroupName(ns)
|
|
switch nodeType {
|
|
case FilerType:
|
|
return cluster.filerGroups.AddClusterNode(filerGroup, nodeType, dataCenter, rack, address, version)
|
|
case BrokerType:
|
|
return cluster.brokerGroups.AddClusterNode(filerGroup, nodeType, dataCenter, rack, address, version)
|
|
case S3Type:
|
|
return cluster.s3Groups.AddClusterNode(filerGroup, nodeType, dataCenter, rack, address, version)
|
|
case VectorType:
|
|
return cluster.vectorGroups.AddClusterNode(filerGroup, nodeType, dataCenter, rack, address, version)
|
|
case MasterType:
|
|
return buildClusterNodeUpdateMessage(true, filerGroup, nodeType, address)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (cluster *Cluster) RemoveClusterNode(ns string, nodeType string, address pb.ServerAddress) []*master_pb.KeepConnectedResponse {
|
|
filerGroup := FilerGroupName(ns)
|
|
switch nodeType {
|
|
case FilerType:
|
|
return cluster.filerGroups.RemoveClusterNode(filerGroup, nodeType, address)
|
|
case BrokerType:
|
|
return cluster.brokerGroups.RemoveClusterNode(filerGroup, nodeType, address)
|
|
case S3Type:
|
|
return cluster.s3Groups.RemoveClusterNode(filerGroup, nodeType, address)
|
|
case VectorType:
|
|
return cluster.vectorGroups.RemoveClusterNode(filerGroup, nodeType, address)
|
|
case MasterType:
|
|
return buildClusterNodeUpdateMessage(false, filerGroup, nodeType, address)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (cluster *Cluster) ListClusterNode(filerGroup FilerGroupName, nodeType string) (nodes []*ClusterNode) {
|
|
switch nodeType {
|
|
case FilerType:
|
|
return cluster.filerGroups.ListClusterNode(filerGroup)
|
|
case BrokerType:
|
|
return cluster.brokerGroups.ListClusterNode(filerGroup)
|
|
case S3Type:
|
|
return cluster.s3Groups.ListClusterNode(filerGroup)
|
|
case VectorType:
|
|
return cluster.vectorGroups.ListClusterNode(filerGroup)
|
|
case MasterType:
|
|
}
|
|
return
|
|
}
|
|
|
|
// ListClusterNodeUpdates reports the current members as add updates, so a
|
|
// client that just connected can rebuild the membership it missed while it was
|
|
// away.
|
|
func (cluster *Cluster) ListClusterNodeUpdates(filerGroup FilerGroupName, nodeType string) (updates []*master_pb.KeepConnectedResponse) {
|
|
for _, node := range cluster.ListClusterNode(filerGroup, nodeType) {
|
|
updates = append(updates, buildClusterNodeUpdateMessage(true, filerGroup, nodeType, node.Address)...)
|
|
}
|
|
return
|
|
}
|
|
|
|
// IsKnownNode reports whether address is currently registered under nodeType
|
|
// in any filer group. The lookup is intentionally group-agnostic because callers
|
|
// (e.g. Ping admission) only know the target address, not the group it joined.
|
|
func (cluster *Cluster) IsKnownNode(nodeType string, address pb.ServerAddress) bool {
|
|
var groups *ClusterNodeGroups
|
|
switch nodeType {
|
|
case FilerType:
|
|
groups = cluster.filerGroups
|
|
case BrokerType:
|
|
groups = cluster.brokerGroups
|
|
case S3Type:
|
|
groups = cluster.s3Groups
|
|
case VectorType:
|
|
groups = cluster.vectorGroups
|
|
default:
|
|
return false
|
|
}
|
|
groups.RLock()
|
|
defer groups.RUnlock()
|
|
for _, members := range groups.groupMembers {
|
|
if _, found := members.members[address]; found {
|
|
return true
|
|
}
|
|
// fall back to a port-tolerant comparison so callers that omit the
|
|
// grpc-port suffix still match a registered peer
|
|
for stored := range members.members {
|
|
if stored.Equals(address) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func buildClusterNodeUpdateMessage(isAdd bool, filerGroup FilerGroupName, nodeType string, address pb.ServerAddress) (result []*master_pb.KeepConnectedResponse) {
|
|
result = append(result, &master_pb.KeepConnectedResponse{
|
|
ClusterNodeUpdate: &master_pb.ClusterNodeUpdate{
|
|
FilerGroup: string(filerGroup),
|
|
NodeType: nodeType,
|
|
Address: string(address),
|
|
IsAdd: isAdd,
|
|
},
|
|
})
|
|
return
|
|
}
|