api/common/id.go (170 lines of code) (raw):
// Licensed to Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Apache Software Foundation (ASF) licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package common
import (
"context"
"fmt"
"net"
"strconv"
"strings"
modelv1 "github.com/apache/skywalking-banyandb/api/proto/banyandb/model/v1"
"github.com/apache/skywalking-banyandb/pkg/convert"
"github.com/apache/skywalking-banyandb/pkg/encoding"
"github.com/apache/skywalking-banyandb/pkg/host"
)
type (
// SeriesID identities a series in a shard.
SeriesID uint64
// ShardID identities a shard in a tsdb.
ShardID uint32
// ItemID identities an item in a series.
ItemID uint64
)
// Marshal encodes series id to bytes.
func (s SeriesID) Marshal() []byte {
return convert.Uint64ToBytes(uint64(s))
}
// AppendToBytes appends series id to bytes.
func (s SeriesID) AppendToBytes(dest []byte) []byte {
return encoding.Uint64ToBytes(dest, uint64(s))
}
// positionKey is a context key to store the module position.
var positionKey = contextPositionKey{}
type contextPositionKey struct{}
// Position is stored in the context.
// The logger could attach it for debugging.
type Position struct {
Module string
Stage string
Database string
Shard string
Segment string
}
// DBLabelNames returns the label names of Position in the database level.
func DBLabelNames() []string {
return []string{"group"}
}
// SegLabelNames returns the label names of Position in the segment level.
func SegLabelNames() []string {
return []string{"seg"}
}
// ShardLabelNames returns the label names of Position. It is used for shard level metrics.
func ShardLabelNames() []string {
return []string{"seg", "shard"}
}
// DBLabelValues returns the label values of Position in the database level.
func (p Position) DBLabelValues() []string {
return []string{p.Database}
}
// SegLabelValues returns the label values of Position.
func (p Position) SegLabelValues() []string {
return []string{p.Segment}
}
// ShardLabelValues returns the label values of Position. It is used for shard level metrics.
func (p Position) ShardLabelValues() []string {
return []string{p.Segment, p.Shard}
}
// String returns the string representation of Position.
func (p Position) String() string {
return fmt.Sprintf("module=%s, stage=%s, database=%s, shard=%s, segment=%s", p.Module, p.Stage, p.Database, p.Shard, p.Segment)
}
// SetPosition sets a position returned from fn to attach it to ctx, then return a new context.
func SetPosition(ctx context.Context, fn func(p Position) Position) context.Context {
val := ctx.Value(positionKey)
var p Position
if val != nil {
p = val.(Position)
}
return context.WithValue(ctx, positionKey, fn(p))
}
// GetPosition returns the position from ctx.
func GetPosition(ctx context.Context) (p Position) {
val := ctx.Value(positionKey)
if val == nil {
return p
}
return val.(Position)
}
// Error wraps a error msg.
type Error struct {
msg string
status modelv1.Status
}
// NewError returns a new Error.
func NewError(tpl string, args ...any) *Error {
return &Error{status: modelv1.Status_STATUS_INTERNAL_ERROR, msg: fmt.Sprintf(tpl, args...)}
}
// NewErrorWithStatus returns a new Error with status.
func NewErrorWithStatus(status modelv1.Status, msg string) *Error {
return &Error{status: status, msg: msg}
}
// Status returns the status.
func (e Error) Status() modelv1.Status {
return e.status
}
// Error returns the error msg.
func (e Error) Error() string {
return fmt.Sprintf("code: %s, msg: %s", modelv1.Status_name[int32(e.status)], e.msg)
}
// Node contains the node id and address.
type Node struct {
Labels map[string]string
NodeID string
GrpcAddress string
HTTPAddress string
}
var (
// FlagNodeHost is the node id from flag.
FlagNodeHost string
// FlagNodeHostProvider is the node id provider from flag.
FlagNodeHostProvider NodeHostProvider
// FlagNodeLabels is the node labels from flag.
FlagNodeLabels []string
)
// NodeHostProvider is the provider of node id.
type NodeHostProvider int
// NodeIDProvider constants.
const (
NodeHostProviderHostname NodeHostProvider = iota
NodeHostProviderIP
NodeHostProviderFlag
)
// String returns the string representation of NodeIDProvider.
func (n *NodeHostProvider) String() string {
return [...]string{"Hostname", "IP", "Flag"}[*n]
}
// ParseNodeHostProvider parses the string to NodeIDProvider.
func ParseNodeHostProvider(s string) (NodeHostProvider, error) {
switch strings.ToLower(s) {
case "hostname":
return NodeHostProviderHostname, nil
case "ip":
return NodeHostProviderIP, nil
case "flag":
return NodeHostProviderFlag, nil
default:
return 0, fmt.Errorf("unknown node id provider %s", s)
}
}
// GenerateNode generates a node id.
func GenerateNode(grpcPort, httpPort *uint32) (node Node, err error) {
port := grpcPort
if port == nil {
port = httpPort
}
if port == nil {
return node, fmt.Errorf("no port found")
}
var nodeHost string
switch FlagNodeHostProvider {
case NodeHostProviderHostname:
h, err := host.Name()
if err != nil {
return node, err
}
nodeHost = h
case NodeHostProviderIP:
ip, err := host.IP()
if err != nil {
return node, err
}
nodeHost = ip
case NodeHostProviderFlag:
nodeHost = FlagNodeHost
default:
return node, fmt.Errorf("unknown node id provider %d", FlagNodeHostProvider)
}
node.NodeID = net.JoinHostPort(nodeHost, strconv.FormatUint(uint64(*port), 10))
if grpcPort != nil {
node.GrpcAddress = net.JoinHostPort(nodeHost, strconv.FormatUint(uint64(*grpcPort), 10))
}
if httpPort != nil {
node.HTTPAddress = net.JoinHostPort(nodeHost, strconv.FormatUint(uint64(*httpPort), 10))
}
node.Labels = ParseNodeFlags()
return node, nil
}
// ParseNodeFlags parses the node labels from flag.
func ParseNodeFlags() map[string]string {
labels := make(map[string]string)
for _, label := range FlagNodeLabels {
parts := strings.Split(label, "=")
if len(parts) != 2 {
continue
}
labels[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
}
return labels
}
// ContextNodeKey is a context key to store the node id.
var ContextNodeKey = contextNodeKey{}
type contextNodeKey struct{}
// ContextNodeRolesKey is a context key to store the node roles.
var ContextNodeRolesKey = contextNodeRolesKey{}
type contextNodeRolesKey struct{}
// ContextNodeSelectorKey is a context key to store the node selector.
var ContextNodeSelectorKey = contextNodeSelectorKey{}
type contextNodeSelectorKey struct{}