ipfs-cluster/informer/disk/disk.go
Hector Sanjuan db00d651bf Balanced allocator: weight-based ordering of partitions
This fixes the issue about partitions not being picked based
on the amount of freespace available in them.

It additionally removes the metrics registry and carries information directly
in the metric.

Metrics have two additional fields: Weight and Partitionable.

Informers have been updated to make use of these fields. Partitions have
weights that equals to the weight of the metrics under them.

Older cluster versions will not set these fields. Partitionable is false by
default and weight has a GetWeight() function to convert value->weight when
unset. This provides backwards compatibility for the freespace metric.
2021-10-06 14:10:06 +02:00

147 lines
3.2 KiB
Go

// Package disk implements an ipfs-cluster informer which can provide different
// disk-related metrics from the IPFS daemon as an api.Metric.
package disk
import (
"context"
"fmt"
"sync"
"github.com/ipfs/ipfs-cluster/api"
logging "github.com/ipfs/go-log/v2"
rpc "github.com/libp2p/go-libp2p-gorpc"
"go.opencensus.io/trace"
)
// MetricType identifies the type of metric to fetch from the IPFS daemon.
type MetricType int
const (
// MetricFreeSpace provides the available space reported by IPFS
MetricFreeSpace MetricType = iota
// MetricRepoSize provides the used space reported by IPFS
MetricRepoSize
)
// String returns a string representation for MetricType.
func (t MetricType) String() string {
switch t {
case MetricFreeSpace:
return "freespace"
case MetricRepoSize:
return "reposize"
}
return ""
}
var logger = logging.Logger("diskinfo")
// Informer is a simple object to implement the ipfscluster.Informer
// and Component interfaces.
type Informer struct {
config *Config // set when created, readonly
mu sync.Mutex // guards access to following fields
rpcClient *rpc.Client
}
// NewInformer returns an initialized informer using the given InformerConfig.
func NewInformer(cfg *Config) (*Informer, error) {
err := cfg.Validate()
if err != nil {
return nil, err
}
return &Informer{
config: cfg,
}, nil
}
// Name returns the name of the metric issued by this informer.
func (disk *Informer) Name() string {
return disk.config.MetricType.String()
}
// SetClient provides us with an rpc.Client which allows
// contacting other components in the cluster.
func (disk *Informer) SetClient(c *rpc.Client) {
disk.mu.Lock()
defer disk.mu.Unlock()
disk.rpcClient = c
}
// Shutdown is called on cluster shutdown. We just invalidate
// any metrics from this point.
func (disk *Informer) Shutdown(ctx context.Context) error {
_, span := trace.StartSpan(ctx, "informer/disk/Shutdown")
defer span.End()
disk.mu.Lock()
defer disk.mu.Unlock()
disk.rpcClient = nil
return nil
}
// GetMetrics returns the metric obtained by this Informer. It must always
// return at least one metric.
func (disk *Informer) GetMetrics(ctx context.Context) []*api.Metric {
ctx, span := trace.StartSpan(ctx, "informer/disk/GetMetric")
defer span.End()
disk.mu.Lock()
rpcClient := disk.rpcClient
disk.mu.Unlock()
if rpcClient == nil {
return []*api.Metric{&api.Metric{
Name: disk.Name(),
Valid: false,
}}
}
var repoStat api.IPFSRepoStat
var metric uint64
valid := true
err := rpcClient.CallContext(
ctx,
"",
"IPFSConnector",
"RepoStat",
struct{}{},
&repoStat,
)
if err != nil {
logger.Error(err)
valid = false
} else {
switch disk.config.MetricType {
case MetricFreeSpace:
size := repoStat.RepoSize
total := repoStat.StorageMax
if size < total {
metric = total - size
} else { // Make sure we don't underflow
metric = 0
}
case MetricRepoSize:
metric = repoStat.RepoSize
}
}
m := &api.Metric{
Name: disk.Name(),
Value: fmt.Sprintf("%d", metric),
Valid: valid,
Weight: int64(metric),
Partitionable: false,
}
m.SetTTL(disk.config.MetricTTL)
return []*api.Metric{m}
}