mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
* Remove Node from data model; standardize on Worker This commit does a lot of things, but in general it attempts to simplify the data model by getting rid of the Node and NodeRole models. Instead, these will use the Worker model, which itself has individual boolean fields for role types. Get rid of roleType in some FreeWorker methods rename NodeService to WorkerRegistry simplify the freeworker interface fix the tests * Remove DeleteWorker method from workerJobService
156 lines
5.3 KiB
Go
156 lines
5.3 KiB
Go
package dax
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/featurebasedb/featurebase/v3/errors"
|
|
)
|
|
|
|
// Node is used in API requests, like RegisterNode (before being assigned
|
|
// roles).
|
|
type Node struct {
|
|
Address Address `json:"address"`
|
|
|
|
// RoleTypes allows a registering node to specify which role type(s) it is
|
|
// capable of filling. The controller will not assign a role to this node
|
|
// with a type not included in RoleTypes.
|
|
RoleTypes []RoleType `json:"role-types"`
|
|
|
|
// HasDirective will be true when the node has received at least one
|
|
// directive from the controller. This can be used to instruct the
|
|
// controller that it should send a directive regardless of whether it
|
|
// already knows about this node. This can happen in an on-prem, serverless
|
|
// setup (when the controller and computer are both running in the same
|
|
// process) and the node is restarted. In that case, the controller comes
|
|
// up, reads the meta data, and assumes that the local computer registering
|
|
// with it has already registered. But we really want the controller to
|
|
// treat this as a new node registration so the computer can load data from
|
|
// snapshotter/writelogger.
|
|
HasDirective bool `json:"has-directive"`
|
|
}
|
|
|
|
// Nodes is a slice of *Node. It's useful for printing the nodes as a list of
|
|
// node.Addresses via its String() method.
|
|
type Nodes []*Node
|
|
|
|
// String prints the slice of node addresses in Nodes.
|
|
func (n Nodes) String() string {
|
|
out := make([]string, 0, len(n))
|
|
for i := range n {
|
|
out = append(out, string(n[i].Address))
|
|
}
|
|
return "[" + strings.Join(out, ",") + "]"
|
|
}
|
|
|
|
// AssignedNode represents a Worker which has been assigned a role. Note that
|
|
// the worker which it represents might be responsible for multiple roles, but
|
|
// AssignedNode only ever represents one of those roles at a time. This is
|
|
// because it is always the response of a RoleType-specific request.
|
|
type AssignedNode struct {
|
|
Address Address `json:"address"`
|
|
Role Role `json:"role"`
|
|
}
|
|
|
|
// WorkerRegistry represents a service for managing Workers.
|
|
type WorkerRegistry interface {
|
|
AddWorker(context.Context, Address, *Node) error
|
|
Worker(context.Context, Address) (*Node, error)
|
|
RemoveWorker(context.Context, Address) error
|
|
Workers(context.Context) ([]*Node, error)
|
|
}
|
|
|
|
// Ensure type implements interface.
|
|
var _ WorkerRegistry = &nopWorkerRegistry{}
|
|
|
|
// nopWorkerRegistry is a no-op implementation of the WorkerRegistry interface.
|
|
type nopWorkerRegistry struct{}
|
|
|
|
func NewNopWorkerRegistry() *nopWorkerRegistry {
|
|
return &nopWorkerRegistry{}
|
|
}
|
|
|
|
func (n *nopWorkerRegistry) AddWorker(context.Context, Address, *Node) error {
|
|
return nil
|
|
}
|
|
func (n *nopWorkerRegistry) Worker(context.Context, Address) (*Node, error) {
|
|
return nil, nil
|
|
}
|
|
func (n *nopWorkerRegistry) RemoveWorker(context.Context, Address) error {
|
|
return nil
|
|
}
|
|
func (n *nopWorkerRegistry) Workers(context.Context) ([]*Node, error) {
|
|
return []*Node{}, nil
|
|
}
|
|
|
|
// ComputeNode represents a compute node and the table/shards for which it is
|
|
// responsible.
|
|
type ComputeNode struct {
|
|
Address Address `json:"address"`
|
|
Table TableKey `json:"table"`
|
|
Shards ShardNums `json:"shards"`
|
|
}
|
|
|
|
// TranslateNode represents a translate node and the table/partitions for which
|
|
// it is responsible.
|
|
type TranslateNode struct {
|
|
Address Address `json:"address"`
|
|
Table TableKey `json:"table"`
|
|
Partitions PartitionNums `json:"partitions"`
|
|
}
|
|
|
|
type Noder interface {
|
|
ComputeNodes(ctx context.Context, qtid QualifiedTableID, shards ...ShardNum) ([]ComputeNode, error)
|
|
TranslateNodes(ctx context.Context, qtid QualifiedTableID, partitions ...PartitionNum) ([]TranslateNode, error)
|
|
|
|
// IngestPartition is effectively the "write" version of TranslateNodes. Its
|
|
// implementations will return the same Address that TranslateNodes would,
|
|
// but it includes the logic to create/assign the partition if it is not
|
|
// already being handled by a computer.
|
|
IngestPartition(ctx context.Context, qtid QualifiedTableID, partition PartitionNum) (Address, error)
|
|
|
|
// IngestShard is effectively the "write" version of ComputeNodes. Its
|
|
// implementations will return the same Address that ComputeNodes would, but
|
|
// it includes the logic to create/assign the shard if it is not already
|
|
// being handled by a computer.
|
|
IngestShard(ctx context.Context, qtid QualifiedTableID, shard ShardNum) (Address, error)
|
|
}
|
|
|
|
// Ensure type implements interface.
|
|
var _ Noder = &nopNoder{}
|
|
|
|
// nopNoder is a no-op implementation of the Noder interface.
|
|
type nopNoder struct{}
|
|
|
|
func NewNopNoder() *nopNoder {
|
|
return &nopNoder{}
|
|
}
|
|
|
|
func (n *nopNoder) ComputeNodes(ctx context.Context, qtid QualifiedTableID, shards ...ShardNum) ([]ComputeNode, error) {
|
|
return nil, nil
|
|
}
|
|
func (n *nopNoder) IngestPartition(ctx context.Context, qtid QualifiedTableID, partition PartitionNum) (Address, error) {
|
|
return "", nil
|
|
}
|
|
func (n *nopNoder) IngestShard(ctx context.Context, qtid QualifiedTableID, shard ShardNum) (Address, error) {
|
|
return "", nil
|
|
}
|
|
func (n *nopNoder) TranslateNodes(ctx context.Context, qtid QualifiedTableID, partitions ...PartitionNum) ([]TranslateNode, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// Errors
|
|
////////////////////////////////////////////////////
|
|
|
|
const (
|
|
ErrNodeDoesNotExist errors.Code = "NodeDoesNotExist"
|
|
)
|
|
|
|
func NewErrNodeDoesNotExist(addr Address) error {
|
|
return errors.New(
|
|
ErrNodeDoesNotExist,
|
|
fmt.Sprintf("node '%s' does not exist", addr),
|
|
)
|
|
}
|