mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
* Support Drop Table in serverless (include Snapshotter, Writelogger) * Finish Database methods Things like: - `Databases` - `DatabaseByID` - `DatabaseByName` - `DropDatabase` * Change Poller to use NodeService instead of its own map Instead of the Poller maintaining its own map of Addresses to poll, this commit changes the Poller to use the NodeService interface to get all known nodes from the Controller. The next commit needs to: Next, the logic in the boltdb NodeService implementation was moved to the boltdb Balancer implementation. That way, the Balancer can be the source of truth for all things nodes/workers/jobs. * Move NodeService from Controller to Balancer This commit moves the implementation of the NodeService into the Balancer, and aligns `Balancer.AddWorker` with `NodeService.CreateNode` so that they stay in sync. (Same for `Balancer.RemoveWorker` and `NodeService.DeleteNode`). * fix import of private repo * Fix go vet issues * Fix bug in DeregisterNode We need to remove the node from the NodeService even if it's not assigned to a database. The logic had a bug in it. This also adds some no-op implementations for SnapshotService and WriteloggerService. If a directory was not configured for that, then the computer node would panic on trying to read from the Snapshotter upon receiving a Directive. * queryer response content-type: json * Add support for NULL to WriteloggerDir and SnapshotterDir configs This commit changes the way WriteLoggerDir and SnapshotterDir are handled. If value is empty `""`, an error will be returned on computer startup. If value is `"NULL"`, a no-op implementation of the service will be used. This would be for a case that wanted to run serverless on-prem with no durable storage. Finally, any other value will be used as the directory to use. Some things which aren't considered here and may result in unexpected behavior: - a value with spaces `" "` - any "null" which is not "NULL"... like lowercase. * Finish the DropTable test * Change "disable service" value to case-insensitive "off" This commit also removes an unnecessary sleep in the tests. * Fix docker-compose variables for IDK test Co-authored-by: Matthew Jaffee <jaffee@pilosa.com>
141 lines
4.4 KiB
Go
141 lines
4.4 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 []RoleType `json:"role-types"`
|
|
}
|
|
|
|
// 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"`
|
|
}
|
|
|
|
// NodeService represents a service for managing Nodes.
|
|
type NodeService interface {
|
|
CreateNode(context.Context, Address, *Node) error
|
|
ReadNode(context.Context, Address) (*Node, error)
|
|
DeleteNode(context.Context, Address) error
|
|
Nodes(context.Context) ([]*Node, error)
|
|
}
|
|
|
|
// Ensure type implements interface.
|
|
var _ NodeService = &nopNodeService{}
|
|
|
|
// nopNoder is a no-op implementation of the Noder interface.
|
|
type nopNodeService struct{}
|
|
|
|
func NewNopNodeService() *nopNodeService {
|
|
return &nopNodeService{}
|
|
}
|
|
|
|
func (n *nopNodeService) CreateNode(context.Context, Address, *Node) error {
|
|
return nil
|
|
}
|
|
func (n *nopNodeService) ReadNode(context.Context, Address) (*Node, error) {
|
|
return nil, nil
|
|
}
|
|
func (n *nopNodeService) DeleteNode(context.Context, Address) error {
|
|
return nil
|
|
}
|
|
func (n *nopNodeService) Nodes(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),
|
|
)
|
|
}
|