ipfs-cluster/ipfsconn/ipfshttp/config.go
Hector Sanjuan acde3f16d0 Fix: repo/stat gets hammered on busy cluster peers
Given that every pin and block/put writes something to IPFS and thus increases
the repo size, a while ago we added a check to let the IPFS connector directly
trigger the sending of metrics every 10 of such requests. This was meant to
update the metrics more often so that balancing happened more granularly
(particularly the freespace one).

In practice, on a cluster that receives several hundreds of pin/adds
operations in a few seconds, this is just bad.

So:

* We disable by default the whole thing.
* We add a new InformerTriggerInterval configuration option to enable the thing.
* Fix a bug that made this always call the first informer, which may not
  have been the freespace one).
2022-02-01 01:34:17 +01:00

231 lines
6.2 KiB
Go

package ipfshttp
import (
"encoding/json"
"errors"
"fmt"
"time"
"github.com/kelseyhightower/envconfig"
"github.com/ipfs/ipfs-cluster/config"
ma "github.com/multiformats/go-multiaddr"
)
const configKey = "ipfshttp"
const envConfigKey = "cluster_ipfshttp"
// Default values for Config.
const (
DefaultNodeAddr = "/ip4/127.0.0.1/tcp/5001"
DefaultConnectSwarmsDelay = 30 * time.Second
DefaultIPFSRequestTimeout = 5 * time.Minute
DefaultPinTimeout = 2 * time.Minute
DefaultUnpinTimeout = 3 * time.Hour
DefaultRepoGCTimeout = 24 * time.Hour
DefaultInformerTriggerInterval = 0 // disabled
DefaultUnpinDisable = false
)
// Config is used to initialize a Connector and allows to customize
// its behaviour. It implements the config.ComponentConfig interface.
type Config struct {
config.Saver
// Host/Port for the IPFS daemon.
NodeAddr ma.Multiaddr
// ConnectSwarmsDelay specifies how long to wait after startup before
// attempting to open connections from this peer's IPFS daemon to the
// IPFS daemons of other peers.
ConnectSwarmsDelay time.Duration
// IPFS Daemon HTTP Client POST timeout
IPFSRequestTimeout time.Duration
// Pin Operation timeout
PinTimeout time.Duration
// Unpin Operation timeout
UnpinTimeout time.Duration
// RepoGC Operation timeout
RepoGCTimeout time.Duration
// How many pin and block/put operations need to happen before we do a
// special broadcast informer metrics to the network. 0 to disable.
InformerTriggerInterval int
// Disables the unpin operation and returns an error.
UnpinDisable bool
// Tracing flag used to skip tracing specific paths when not enabled.
Tracing bool
}
type jsonConfig struct {
NodeMultiaddress string `json:"node_multiaddress"`
ConnectSwarmsDelay string `json:"connect_swarms_delay"`
IPFSRequestTimeout string `json:"ipfs_request_timeout"`
PinTimeout string `json:"pin_timeout"`
UnpinTimeout string `json:"unpin_timeout"`
RepoGCTimeout string `json:"repogc_timeout"`
InformerTriggerInterval int `json:"informer_trigger_interval"`
UnpinDisable bool `json:"unpin_disable,omitempty"`
}
// ConfigKey provides a human-friendly identifier for this type of Config.
func (cfg *Config) ConfigKey() string {
return configKey
}
// Default sets the fields of this Config to sensible default values.
func (cfg *Config) Default() error {
node, _ := ma.NewMultiaddr(DefaultNodeAddr)
cfg.NodeAddr = node
cfg.ConnectSwarmsDelay = DefaultConnectSwarmsDelay
cfg.IPFSRequestTimeout = DefaultIPFSRequestTimeout
cfg.PinTimeout = DefaultPinTimeout
cfg.UnpinTimeout = DefaultUnpinTimeout
cfg.RepoGCTimeout = DefaultRepoGCTimeout
cfg.InformerTriggerInterval = DefaultInformerTriggerInterval
cfg.UnpinDisable = DefaultUnpinDisable
return nil
}
// ApplyEnvVars fills in any Config fields found
// as environment variables.
func (cfg *Config) ApplyEnvVars() error {
jcfg, err := cfg.toJSONConfig()
if err != nil {
return err
}
err = envconfig.Process(envConfigKey, jcfg)
if err != nil {
return err
}
return cfg.applyJSONConfig(jcfg)
}
// Validate checks that the fields of this Config have sensible values,
// at least in appearance.
func (cfg *Config) Validate() error {
var err error
if cfg.NodeAddr == nil {
err = errors.New("ipfshttp.node_multiaddress not set")
}
if cfg.ConnectSwarmsDelay < 0 {
err = errors.New("ipfshttp.connect_swarms_delay is invalid")
}
if cfg.IPFSRequestTimeout < 0 {
err = errors.New("ipfshttp.ipfs_request_timeout invalid")
}
if cfg.PinTimeout < 0 {
err = errors.New("ipfshttp.pin_timeout invalid")
}
if cfg.UnpinTimeout < 0 {
err = errors.New("ipfshttp.unpin_timeout invalid")
}
if cfg.RepoGCTimeout < 0 {
err = errors.New("ipfshttp.repogc_timeout invalid")
}
if cfg.InformerTriggerInterval < 0 {
err = errors.New("ipfshttp.update_metrics_after")
}
return err
}
// LoadJSON parses a JSON representation of this Config as generated by ToJSON.
func (cfg *Config) LoadJSON(raw []byte) error {
jcfg := &jsonConfig{}
err := json.Unmarshal(raw, jcfg)
if err != nil {
logger.Error("Error unmarshaling ipfshttp config")
return err
}
cfg.Default()
return cfg.applyJSONConfig(jcfg)
}
func (cfg *Config) applyJSONConfig(jcfg *jsonConfig) error {
nodeAddr, err := ma.NewMultiaddr(jcfg.NodeMultiaddress)
if err != nil {
return fmt.Errorf("error parsing ipfs_node_multiaddress: %s", err)
}
cfg.NodeAddr = nodeAddr
cfg.UnpinDisable = jcfg.UnpinDisable
cfg.InformerTriggerInterval = jcfg.InformerTriggerInterval
err = config.ParseDurations(
"ipfshttp",
&config.DurationOpt{Duration: jcfg.ConnectSwarmsDelay, Dst: &cfg.ConnectSwarmsDelay, Name: "connect_swarms_delay"},
&config.DurationOpt{Duration: jcfg.IPFSRequestTimeout, Dst: &cfg.IPFSRequestTimeout, Name: "ipfs_request_timeout"},
&config.DurationOpt{Duration: jcfg.PinTimeout, Dst: &cfg.PinTimeout, Name: "pin_timeout"},
&config.DurationOpt{Duration: jcfg.UnpinTimeout, Dst: &cfg.UnpinTimeout, Name: "unpin_timeout"},
&config.DurationOpt{Duration: jcfg.RepoGCTimeout, Dst: &cfg.RepoGCTimeout, Name: "repogc_timeout"},
)
if err != nil {
return err
}
return cfg.Validate()
}
// ToJSON generates a human-friendly JSON representation of this Config.
func (cfg *Config) ToJSON() (raw []byte, err error) {
jcfg, err := cfg.toJSONConfig()
if err != nil {
return
}
raw, err = config.DefaultJSONMarshal(jcfg)
return
}
func (cfg *Config) toJSONConfig() (jcfg *jsonConfig, err error) {
// Multiaddress String() may panic
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%s", r)
}
}()
jcfg = &jsonConfig{}
// Set all configuration fields
jcfg.NodeMultiaddress = cfg.NodeAddr.String()
jcfg.ConnectSwarmsDelay = cfg.ConnectSwarmsDelay.String()
jcfg.IPFSRequestTimeout = cfg.IPFSRequestTimeout.String()
jcfg.PinTimeout = cfg.PinTimeout.String()
jcfg.UnpinTimeout = cfg.UnpinTimeout.String()
jcfg.RepoGCTimeout = cfg.RepoGCTimeout.String()
jcfg.InformerTriggerInterval = cfg.InformerTriggerInterval
jcfg.UnpinDisable = cfg.UnpinDisable
return
}
// ToDisplayJSON returns JSON config as a string.
func (cfg *Config) ToDisplayJSON() ([]byte, error) {
jcfg, err := cfg.toJSONConfig()
if err != nil {
return nil, err
}
return config.DisplayJSON(jcfg)
}