diff --git a/cmd/waku/flags.go b/cmd/waku/flags.go index cf6ad149..3bd20b8d 100644 --- a/cmd/waku/flags.go +++ b/cmd/waku/flags.go @@ -174,6 +174,20 @@ var ( Destination: &options.CircuitRelay, EnvVars: []string{"WAKUNODE2_CIRCUIT_RELAY"}, }) + ResourceScalingMemoryPercent = altsrc.NewFloat64Flag(&cli.Float64Flag{ + Name: "resource-scaling-memory-percentage", + Usage: "Determines the percentage of total accessible memory that wil be dedicated to go-waku. A dedicated node with a lot of RAM could allocate 25% or more memory to go-waku", + Value: 25, + Destination: &options.ResourceScalingMemoryPercent, + EnvVars: []string{"WAKUNODE2_RESOURCE_MEMORY_PERCENTAGE"}, + }) + ResourceScalingFDPercent = altsrc.NewFloat64Flag(&cli.Float64Flag{ + Name: "resource-scaling-file-descriptors-percentage", + Usage: "Determines the percentage of total file descriptors that wil be dedicated to go-waku.", + Value: 50, + Destination: &options.ResourceScalingFDPercent, + EnvVars: []string{"WAKUNODE2_RESOURCE_FD_PERCENTAGE"}, + }) LogLevel = cliutils.NewGenericFlagSingleValue(&cli.GenericFlag{ Name: "log-level", Aliases: []string{"l"}, diff --git a/cmd/waku/main.go b/cmd/waku/main.go index 5046bff9..c2469645 100644 --- a/cmd/waku/main.go +++ b/cmd/waku/main.go @@ -41,6 +41,8 @@ func main() { ExtMultiaddresses, ShowAddresses, CircuitRelay, + ResourceScalingMemoryPercent, + ResourceScalingFDPercent, LogLevel, LogEncoding, LogOutput, diff --git a/flake.nix b/flake.nix index a457f78c..e6149406 100644 --- a/flake.nix +++ b/flake.nix @@ -28,7 +28,7 @@ ]; doCheck = false; # FIXME: This needs to be manually changed when updating modules. - vendorSha256 = "sha256-o8SeaxD5CDhYqMEr7OtaD0CerwO9HAbA0IJnbDjSLBM="; + vendorSha256 = "sha256-bDiX2+o0oXx3KvsolcrQriPYnGzWKY3fz3T+fGtVRpI="; # Fix for 'nix run' trying to execute 'go-waku'. meta = { mainProgram = "waku"; }; }; diff --git a/waku/node.go b/waku/node.go index 2e99ca54..d7340010 100644 --- a/waku/node.go +++ b/waku/node.go @@ -14,6 +14,9 @@ import ( "syscall" "time" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" + "github.com/pbnjay/memory" + wmetrics "github.com/waku-org/go-waku/waku/v2/metrics" "github.com/waku-org/go-waku/waku/v2/rendezvous" @@ -62,6 +65,18 @@ func requiresDB(options Options) bool { return options.Store.Enable || options.Rendezvous.Server } +func scalePerc(value float64) float64 { + if value > 100 { + return 100 + } + + if value < 0.1 { + return 0.1 + } + + return value +} + const dialTimeout = 7 * time.Second // Execute starts a go-waku node with settings determined by the Options parameter @@ -129,6 +144,17 @@ func Execute(options Options) { } libp2pOpts := node.DefaultLibP2POptions + + memPerc := scalePerc(options.ResourceScalingMemoryPercent) + fdPerc := scalePerc(options.ResourceScalingFDPercent) + limits := rcmgr.DefaultLimits // Default memory limit: 1/8th of total memory, minimum 128MB, maximum 1GB + scaledLimits := limits.Scale(int64(float64(memory.TotalMemory())*memPerc/100), int(float64(getNumFDs())*fdPerc/100)) + resourceManager, err := rcmgr.NewResourceManager(rcmgr.NewFixedLimiter(scaledLimits)) + failOnErr(err, "setting resource limits") + + libp2pOpts = append(libp2pOpts, libp2p.ResourceManager(resourceManager)) + libp2p.SetDefaultServiceLimits(&limits) + if len(options.AdvertiseAddresses) == 0 { libp2pOpts = append(libp2pOpts, libp2p.NATPortMap()) // Attempt to open ports using uPNP for NATed hosts.) } diff --git a/waku/options.go b/waku/options.go index a004e038..2d964187 100644 --- a/waku/options.go +++ b/waku/options.go @@ -143,27 +143,29 @@ type RendezvousOptions struct { // Options contains all the available features and settings that can be // configured via flags when executing go-waku as a service. type Options struct { - Port int - Address string - Dns4DomainName string - NodeKey *ecdsa.PrivateKey - KeyFile string - KeyPasswd string - GenerateKey bool - Overwrite bool - StaticNodes []multiaddr.Multiaddr - KeepAlive time.Duration - AdvertiseAddresses []multiaddr.Multiaddr - ShowAddresses bool - CircuitRelay bool - LogLevel string - LogEncoding string - LogOutput string - NAT string - ExtIP string - PersistPeers bool - UserAgent string - PProf bool + Port int + Address string + Dns4DomainName string + NodeKey *ecdsa.PrivateKey + KeyFile string + KeyPasswd string + GenerateKey bool + Overwrite bool + StaticNodes []multiaddr.Multiaddr + KeepAlive time.Duration + AdvertiseAddresses []multiaddr.Multiaddr + ShowAddresses bool + CircuitRelay bool + ResourceScalingMemoryPercent float64 + ResourceScalingFDPercent float64 + LogLevel string + LogEncoding string + LogOutput string + NAT string + ExtIP string + PersistPeers bool + UserAgent string + PProf bool PeerExchange PeerExchangeOptions Websocket WSOptions diff --git a/waku/sys_not_unix.go b/waku/sys_not_unix.go new file mode 100644 index 00000000..23028ce5 --- /dev/null +++ b/waku/sys_not_unix.go @@ -0,0 +1,11 @@ +//go:build !linux && !darwin && !windows + +package waku + +import "runtime" + +// TODO: figure out how to get the number of file descriptors on Windows and other systems +func getNumFDs() int { + fmt.Println("cannot determine number of file descriptors on ", runtime.GOOS) + return 0 +} diff --git a/waku/sys_unix.go b/waku/sys_unix.go new file mode 100644 index 00000000..473fd877 --- /dev/null +++ b/waku/sys_unix.go @@ -0,0 +1,18 @@ +//go:build linux || darwin + +package waku + +import ( + "fmt" + + "golang.org/x/sys/unix" +) + +func getNumFDs() int { + var l unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &l); err != nil { + fmt.Println("failed to get fd limit:" + err.Error()) + return 0 + } + return int(l.Cur) +} diff --git a/waku/sys_windows.go b/waku/sys_windows.go new file mode 100644 index 00000000..ad5e36f8 --- /dev/null +++ b/waku/sys_windows.go @@ -0,0 +1,11 @@ +//go:build windows + +package waku + +import ( + "math" +) + +func getNumFDs() int { + return math.MaxInt +}