storagemitner: manage ulimit on start
This commit is contained in:
parent
09c9fcce00
commit
df9aa0e357
@ -20,6 +20,7 @@ import (
|
||||
lcli "github.com/filecoin-project/lotus/cli"
|
||||
"github.com/filecoin-project/lotus/lib/auth"
|
||||
"github.com/filecoin-project/lotus/lib/jsonrpc"
|
||||
"github.com/filecoin-project/lotus/lib/ulimit"
|
||||
"github.com/filecoin-project/lotus/node"
|
||||
"github.com/filecoin-project/lotus/node/impl"
|
||||
"github.com/filecoin-project/lotus/node/modules/dtypes"
|
||||
@ -57,6 +58,10 @@ var runCmd = &cli.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, err := ulimit.ManageFdLimit(); err != nil {
|
||||
log.Errorf("setting file descriptor limit: %s", err)
|
||||
}
|
||||
|
||||
if v.APIVersion != build.APIVersion {
|
||||
return xerrors.Errorf("lotus-daemon API version doesn't match: local: %s", api.Version{APIVersion: build.APIVersion})
|
||||
}
|
||||
|
1
go.mod
1
go.mod
@ -102,6 +102,7 @@ require (
|
||||
go.uber.org/goleak v1.0.0 // indirect
|
||||
go.uber.org/multierr v1.4.0
|
||||
go.uber.org/zap v1.13.0
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
gopkg.in/urfave/cli.v2 v2.0.0-20180128182452-d3ae77c26ac8
|
||||
|
116
lib/ulimit/ulimit.go
Normal file
116
lib/ulimit/ulimit.go
Normal file
@ -0,0 +1,116 @@
|
||||
package ulimit
|
||||
|
||||
// from go-ipfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
)
|
||||
|
||||
var log = logging.Logger("ulimit")
|
||||
|
||||
var (
|
||||
supportsFDManagement = false
|
||||
|
||||
// getlimit returns the soft and hard limits of file descriptors counts
|
||||
getLimit func() (uint64, uint64, error)
|
||||
// set limit sets the soft and hard limits of file descriptors counts
|
||||
setLimit func(uint64, uint64) error
|
||||
)
|
||||
|
||||
// minimum file descriptor limit before we complain
|
||||
const minFds = 2048
|
||||
|
||||
// default max file descriptor limit.
|
||||
const maxFds = 16 << 10
|
||||
|
||||
// userMaxFDs returns the value of IPFS_FD_MAX
|
||||
func userMaxFDs() uint64 {
|
||||
// check if the IPFS_FD_MAX is set up and if it does
|
||||
// not have a valid fds number notify the user
|
||||
if val := os.Getenv("IPFS_FD_MAX"); val != "" {
|
||||
fds, err := strconv.ParseUint(val, 10, 64)
|
||||
if err != nil {
|
||||
log.Errorf("bad value for IPFS_FD_MAX: %s", err)
|
||||
return 0
|
||||
}
|
||||
return fds
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// ManageFdLimit raise the current max file descriptor count
|
||||
// of the process based on the IPFS_FD_MAX value
|
||||
func ManageFdLimit() (changed bool, newLimit uint64, err error) {
|
||||
if !supportsFDManagement {
|
||||
return false, 0, nil
|
||||
}
|
||||
|
||||
targetLimit := uint64(maxFds)
|
||||
userLimit := userMaxFDs()
|
||||
if userLimit > 0 {
|
||||
targetLimit = userLimit
|
||||
}
|
||||
|
||||
soft, hard, err := getLimit()
|
||||
if err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
|
||||
if targetLimit <= soft {
|
||||
return false, 0, nil
|
||||
}
|
||||
|
||||
// the soft limit is the value that the kernel enforces for the
|
||||
// corresponding resource
|
||||
// the hard limit acts as a ceiling for the soft limit
|
||||
// an unprivileged process may only set it's soft limit to a
|
||||
// alue in the range from 0 up to the hard limit
|
||||
err = setLimit(targetLimit, targetLimit)
|
||||
switch err {
|
||||
case nil:
|
||||
newLimit = targetLimit
|
||||
case syscall.EPERM:
|
||||
// lower limit if necessary.
|
||||
if targetLimit > hard {
|
||||
targetLimit = hard
|
||||
}
|
||||
|
||||
// the process does not have permission so we should only
|
||||
// set the soft value
|
||||
err = setLimit(targetLimit, hard)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error setting ulimit wihout hard limit: %s", err)
|
||||
break
|
||||
}
|
||||
newLimit = targetLimit
|
||||
|
||||
// Warn on lowered limit.
|
||||
|
||||
if newLimit < userLimit {
|
||||
err = fmt.Errorf(
|
||||
"failed to raise ulimit to IPFS_FD_MAX (%d): set to %d",
|
||||
userLimit,
|
||||
newLimit,
|
||||
)
|
||||
break
|
||||
}
|
||||
|
||||
if userLimit == 0 && newLimit < minFds {
|
||||
err = fmt.Errorf(
|
||||
"failed to raise ulimit to minimum %d: set to %d",
|
||||
minFds,
|
||||
newLimit,
|
||||
)
|
||||
break
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("error setting: ulimit: %s", err)
|
||||
}
|
||||
|
||||
return newLimit > 0, newLimit, err
|
||||
}
|
36
lib/ulimit/ulimit_freebsd.go
Normal file
36
lib/ulimit/ulimit_freebsd.go
Normal file
@ -0,0 +1,36 @@
|
||||
// +build freebsd
|
||||
|
||||
package ulimit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
unix "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func init() {
|
||||
supportsFDManagement = true
|
||||
getLimit = freebsdGetLimit
|
||||
setLimit = freebsdSetLimit
|
||||
}
|
||||
|
||||
func freebsdGetLimit() (uint64, uint64, error) {
|
||||
rlimit := unix.Rlimit{}
|
||||
err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit)
|
||||
if (rlimit.Cur < 0) || (rlimit.Max < 0) {
|
||||
return 0, 0, errors.New("invalid rlimits")
|
||||
}
|
||||
return uint64(rlimit.Cur), uint64(rlimit.Max), err
|
||||
}
|
||||
|
||||
func freebsdSetLimit(soft uint64, max uint64) error {
|
||||
if (soft > math.MaxInt64) || (max > math.MaxInt64) {
|
||||
return errors.New("invalid rlimits")
|
||||
}
|
||||
rlimit := unix.Rlimit{
|
||||
Cur: int64(soft),
|
||||
Max: int64(max),
|
||||
}
|
||||
return unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit)
|
||||
}
|
84
lib/ulimit/ulimit_test.go
Normal file
84
lib/ulimit/ulimit_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
// +build !windows
|
||||
|
||||
package ulimit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestManageFdLimit(t *testing.T) {
|
||||
t.Log("Testing file descriptor count")
|
||||
if _, _, err := ManageFdLimit(); err != nil {
|
||||
t.Errorf("Cannot manage file descriptors")
|
||||
}
|
||||
|
||||
if maxFds != uint64(16 << 10) {
|
||||
t.Errorf("Maximum file descriptors default value changed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManageInvalidNFds(t *testing.T) {
|
||||
t.Logf("Testing file descriptor invalidity")
|
||||
var err error
|
||||
if err = os.Unsetenv("IPFS_FD_MAX"); err != nil {
|
||||
t.Fatal("Cannot unset the IPFS_FD_MAX env variable")
|
||||
}
|
||||
|
||||
rlimit := syscall.Rlimit{}
|
||||
if err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil {
|
||||
t.Fatal("Cannot get the file descriptor count")
|
||||
}
|
||||
|
||||
value := rlimit.Max + rlimit.Cur
|
||||
if err = os.Setenv("IPFS_FD_MAX", fmt.Sprintf("%d", value)); err != nil {
|
||||
t.Fatal("Cannot set the IPFS_FD_MAX env variable")
|
||||
}
|
||||
|
||||
t.Logf("setting ulimit to %d, max %d, cur %d", value, rlimit.Max, rlimit.Cur)
|
||||
|
||||
if changed, new, err := ManageFdLimit(); err == nil {
|
||||
t.Errorf("ManageFdLimit should return an error: changed %t, new: %d", changed, new)
|
||||
} else if err != nil {
|
||||
flag := strings.Contains(err.Error(),
|
||||
"failed to raise ulimit to IPFS_FD_MAX")
|
||||
if !flag {
|
||||
t.Error("ManageFdLimit returned unexpected error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// unset all previous operations
|
||||
if err = os.Unsetenv("IPFS_FD_MAX"); err != nil {
|
||||
t.Fatal("Cannot unset the IPFS_FD_MAX env variable")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManageFdLimitWithEnvSet(t *testing.T) {
|
||||
t.Logf("Testing file descriptor manager with IPFS_FD_MAX set")
|
||||
var err error
|
||||
if err = os.Unsetenv("IPFS_FD_MAX"); err != nil {
|
||||
t.Fatal("Cannot unset the IPFS_FD_MAX env variable")
|
||||
}
|
||||
|
||||
rlimit := syscall.Rlimit{}
|
||||
if err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil {
|
||||
t.Fatal("Cannot get the file descriptor count")
|
||||
}
|
||||
|
||||
value := rlimit.Max - rlimit.Cur + 1
|
||||
if err = os.Setenv("IPFS_FD_MAX", fmt.Sprintf("%d", value)); err != nil {
|
||||
t.Fatal("Cannot set the IPFS_FD_MAX env variable")
|
||||
}
|
||||
|
||||
if _, _, err = ManageFdLimit(); err != nil {
|
||||
t.Errorf("Cannot manage file descriptor count")
|
||||
}
|
||||
|
||||
// unset all previous operations
|
||||
if err = os.Unsetenv("IPFS_FD_MAX"); err != nil {
|
||||
t.Fatal("Cannot unset the IPFS_FD_MAX env variable")
|
||||
}
|
||||
}
|
27
lib/ulimit/ulimit_unix.go
Normal file
27
lib/ulimit/ulimit_unix.go
Normal file
@ -0,0 +1,27 @@
|
||||
// +build darwin linux netbsd openbsd
|
||||
|
||||
package ulimit
|
||||
|
||||
import (
|
||||
unix "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func init() {
|
||||
supportsFDManagement = true
|
||||
getLimit = unixGetLimit
|
||||
setLimit = unixSetLimit
|
||||
}
|
||||
|
||||
func unixGetLimit() (uint64, uint64, error) {
|
||||
rlimit := unix.Rlimit{}
|
||||
err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit)
|
||||
return rlimit.Cur, rlimit.Max, err
|
||||
}
|
||||
|
||||
func unixSetLimit(soft uint64, max uint64) error {
|
||||
rlimit := unix.Rlimit{
|
||||
Cur: soft,
|
||||
Max: max,
|
||||
}
|
||||
return unix.Setrlimit(unix.RLIMIT_NOFILE, &rlimit)
|
||||
}
|
Loading…
Reference in New Issue
Block a user