2018-10-08 23:15:54 +01:00
|
|
|
// Copyright (C) 2018 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package sdbclient
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
"google.golang.org/grpc"
|
2018-11-09 13:32:35 +00:00
|
|
|
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
2018-10-08 23:15:54 +01:00
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
"storj.io/storj/pkg/auth/grpcauth"
|
2018-10-08 23:15:54 +01:00
|
|
|
"storj.io/storj/pkg/provider"
|
|
|
|
pb "storj.io/storj/pkg/statdb/proto"
|
2018-11-09 13:32:35 +00:00
|
|
|
"storj.io/storj/pkg/transport"
|
2018-10-08 23:15:54 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
mon = monkit.Package()
|
|
|
|
)
|
|
|
|
|
|
|
|
// StatDB creates a grpcClient
|
|
|
|
type StatDB struct {
|
2018-11-06 17:49:17 +00:00
|
|
|
client pb.StatDBClient
|
|
|
|
APIKey []byte
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Client services offerred for the interface
|
|
|
|
type Client interface {
|
|
|
|
Create(ctx context.Context, nodeID []byte) error
|
2018-11-15 00:03:19 +00:00
|
|
|
CreateWithStats(ctx context.Context, nodeID []byte, stats *pb.NodeStats) error
|
2018-10-08 23:15:54 +01:00
|
|
|
Get(ctx context.Context, nodeID []byte) (*pb.NodeStats, error)
|
2018-11-15 00:03:19 +00:00
|
|
|
FindValidNodes(ctx context.Context, nodeIDs [][]byte, minStats *pb.NodeStats) (passedIDs [][]byte, err error)
|
|
|
|
Update(ctx context.Context, nodeID []byte, auditSuccess, isUp bool,
|
|
|
|
latencyList []int64) (stats *pb.NodeStats, err error)
|
|
|
|
UpdateUptime(ctx context.Context, nodeID []byte, isUp bool) (*pb.NodeStats, error)
|
|
|
|
UpdateAuditSuccess(ctx context.Context, nodeID []byte, passed bool) (*pb.NodeStats, error)
|
2018-10-16 18:40:34 +01:00
|
|
|
UpdateBatch(ctx context.Context, nodes []*pb.Node) ([]*pb.NodeStats, []*pb.Node, error)
|
2018-11-15 00:03:19 +00:00
|
|
|
CreateEntryIfNotExists(ctx context.Context, nodeID []byte) (stats *pb.NodeStats, err error)
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewClient initializes a new statdb client
|
2018-11-26 17:08:29 +00:00
|
|
|
func NewClient(identity *provider.FullIdentity, address string, APIKey string) (Client, error) {
|
|
|
|
apiKeyInjector := grpcauth.NewAPIKeyInjector(APIKey)
|
2018-11-06 17:49:17 +00:00
|
|
|
tc := transport.NewClient(identity)
|
2018-11-26 17:08:29 +00:00
|
|
|
conn, err := tc.DialAddress(
|
|
|
|
context.Background(),
|
|
|
|
address,
|
|
|
|
grpc.WithUnaryInterceptor(apiKeyInjector),
|
|
|
|
)
|
2018-10-08 23:15:54 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
return &StatDB{client: pb.NewStatDBClient(conn)}, nil
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// a compiler trick to make sure *StatDB implements Client
|
|
|
|
var _ Client = (*StatDB)(nil)
|
|
|
|
|
2018-11-15 00:03:19 +00:00
|
|
|
// Create is used for creating a new entry in the stats db with default reputation
|
2018-10-08 23:15:54 +01:00
|
|
|
func (sdb *StatDB) Create(ctx context.Context, nodeID []byte) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
node := pb.Node{
|
2018-11-15 00:03:19 +00:00
|
|
|
NodeId: nodeID,
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|
|
|
|
createReq := &pb.CreateRequest{
|
2018-11-26 17:08:29 +00:00
|
|
|
Node: &node,
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|
2018-11-06 17:49:17 +00:00
|
|
|
_, err = sdb.client.Create(ctx, createReq)
|
2018-10-08 23:15:54 +01:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-11-15 00:03:19 +00:00
|
|
|
// CreateWithStats is used for creating a new entry in the stats db with a specific reputation
|
|
|
|
// stats must have AuditCount, AuditSuccessCount, UptimeCount, UptimeSuccessCount
|
|
|
|
func (sdb *StatDB) CreateWithStats(ctx context.Context, nodeID []byte, stats *pb.NodeStats) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
node := &pb.Node{
|
|
|
|
NodeId: nodeID,
|
|
|
|
}
|
|
|
|
createReq := &pb.CreateRequest{
|
2018-11-26 17:08:29 +00:00
|
|
|
Node: node,
|
|
|
|
Stats: stats,
|
2018-11-15 00:03:19 +00:00
|
|
|
}
|
|
|
|
_, err = sdb.client.Create(ctx, createReq)
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-10-08 23:15:54 +01:00
|
|
|
// Get is used for retrieving a new entry from the stats db
|
|
|
|
func (sdb *StatDB) Get(ctx context.Context, nodeID []byte) (stats *pb.NodeStats, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
getReq := &pb.GetRequest{
|
|
|
|
NodeId: nodeID,
|
|
|
|
}
|
2018-11-06 17:49:17 +00:00
|
|
|
res, err := sdb.client.Get(ctx, getReq)
|
2018-11-07 01:16:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-10-08 23:15:54 +01:00
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
return res.Stats, nil
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|
|
|
|
|
2018-10-30 17:11:22 +00:00
|
|
|
// FindValidNodes is used for retrieving a subset of nodes that meet a minimum reputation requirement
|
2018-11-15 00:03:19 +00:00
|
|
|
// minStats must have AuditSuccessRatio, UptimeRatio, AuditCount
|
|
|
|
func (sdb *StatDB) FindValidNodes(ctx context.Context, nodeIDs [][]byte,
|
|
|
|
minStats *pb.NodeStats) (passedIDs [][]byte, err error) {
|
2018-10-30 17:11:22 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
findValidNodesReq := &pb.FindValidNodesRequest{
|
2018-11-15 00:03:19 +00:00
|
|
|
NodeIds: nodeIDs,
|
|
|
|
MinStats: minStats,
|
2018-10-30 17:11:22 +00:00
|
|
|
}
|
|
|
|
|
2018-11-06 17:49:17 +00:00
|
|
|
res, err := sdb.client.FindValidNodes(ctx, findValidNodesReq)
|
2018-10-30 17:11:22 +00:00
|
|
|
if err != nil {
|
2018-10-31 16:18:51 +00:00
|
|
|
return nil, err
|
2018-10-30 17:11:22 +00:00
|
|
|
}
|
|
|
|
|
2018-10-31 16:18:51 +00:00
|
|
|
return res.PassedIds, nil
|
2018-10-30 17:11:22 +00:00
|
|
|
}
|
|
|
|
|
2018-10-08 23:15:54 +01:00
|
|
|
// Update is used for updating a node's stats in the stats db
|
2018-11-15 00:03:19 +00:00
|
|
|
func (sdb *StatDB) Update(ctx context.Context, nodeID []byte,
|
|
|
|
auditSuccess, isUp bool, latencyList []int64) (stats *pb.NodeStats, err error) {
|
2018-10-08 23:15:54 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
node := pb.Node{
|
|
|
|
NodeId: nodeID,
|
|
|
|
AuditSuccess: auditSuccess,
|
|
|
|
IsUp: isUp,
|
|
|
|
LatencyList: latencyList,
|
2018-11-15 00:03:19 +00:00
|
|
|
UpdateAuditSuccess: true,
|
|
|
|
UpdateUptime: true,
|
|
|
|
UpdateLatency: true,
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|
|
|
|
updateReq := &pb.UpdateRequest{
|
2018-11-26 17:08:29 +00:00
|
|
|
Node: &node,
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|
|
|
|
|
2018-11-06 17:49:17 +00:00
|
|
|
res, err := sdb.client.Update(ctx, updateReq)
|
2018-11-07 01:16:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-10-08 23:15:54 +01:00
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
return res.Stats, nil
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|
|
|
|
|
2018-11-15 00:03:19 +00:00
|
|
|
// UpdateUptime is used for updating a node's uptime in statdb
|
|
|
|
func (sdb *StatDB) UpdateUptime(ctx context.Context, nodeID []byte,
|
|
|
|
isUp bool) (stats *pb.NodeStats, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
node := pb.Node{
|
2018-11-26 17:08:29 +00:00
|
|
|
NodeId: nodeID,
|
|
|
|
IsUp: isUp,
|
2018-11-15 00:03:19 +00:00
|
|
|
}
|
2018-11-26 17:08:29 +00:00
|
|
|
updateReq := &pb.UpdateUptimeRequest{
|
|
|
|
Node: &node,
|
2018-11-15 00:03:19 +00:00
|
|
|
}
|
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
res, err := sdb.client.UpdateUptime(ctx, updateReq)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-11-15 00:03:19 +00:00
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
return res.Stats, nil
|
2018-11-15 00:03:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// UpdateAuditSuccess is used for updating a node's audit success in statdb
|
|
|
|
func (sdb *StatDB) UpdateAuditSuccess(ctx context.Context, nodeID []byte,
|
|
|
|
passed bool) (stats *pb.NodeStats, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
node := pb.Node{
|
2018-11-26 17:08:29 +00:00
|
|
|
NodeId: nodeID,
|
|
|
|
AuditSuccess: passed,
|
2018-11-15 00:03:19 +00:00
|
|
|
}
|
2018-11-26 17:08:29 +00:00
|
|
|
updateReq := &pb.UpdateAuditSuccessRequest{
|
|
|
|
Node: &node,
|
2018-11-15 00:03:19 +00:00
|
|
|
}
|
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
res, err := sdb.client.UpdateAuditSuccess(ctx, updateReq)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-11-15 00:03:19 +00:00
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
return res.Stats, nil
|
2018-11-15 00:03:19 +00:00
|
|
|
}
|
|
|
|
|
2018-10-08 23:15:54 +01:00
|
|
|
// UpdateBatch is used for updating multiple nodes' stats in the stats db
|
2018-10-16 18:40:34 +01:00
|
|
|
func (sdb *StatDB) UpdateBatch(ctx context.Context, nodes []*pb.Node) (statsList []*pb.NodeStats, failedNodes []*pb.Node, err error) {
|
2018-10-08 23:15:54 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
updateBatchReq := &pb.UpdateBatchRequest{
|
|
|
|
NodeList: nodes,
|
|
|
|
}
|
|
|
|
|
2018-11-06 17:49:17 +00:00
|
|
|
res, err := sdb.client.UpdateBatch(ctx, updateBatchReq)
|
2018-11-07 01:16:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2018-10-08 23:15:54 +01:00
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
return res.StatsList, res.FailedNodes, nil
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// CreateEntryIfNotExists creates a db entry for a node if entry doesn't already exist
|
2018-11-15 00:03:19 +00:00
|
|
|
func (sdb *StatDB) CreateEntryIfNotExists(ctx context.Context, nodeID []byte) (stats *pb.NodeStats, err error) {
|
2018-10-16 18:40:34 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2018-11-15 00:03:19 +00:00
|
|
|
node := &pb.Node{NodeId: nodeID}
|
2018-10-16 18:40:34 +01:00
|
|
|
createReq := &pb.CreateEntryIfNotExistsRequest{
|
2018-11-26 17:08:29 +00:00
|
|
|
Node: node,
|
2018-10-16 18:40:34 +01:00
|
|
|
}
|
|
|
|
|
2018-11-06 17:49:17 +00:00
|
|
|
res, err := sdb.client.CreateEntryIfNotExists(ctx, createReq)
|
2018-11-07 01:16:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-10-16 18:40:34 +01:00
|
|
|
|
2018-11-26 17:08:29 +00:00
|
|
|
return res.Stats, nil
|
2018-10-08 23:15:54 +01:00
|
|
|
}
|