Skip to content

Commit b0aeecb

Browse files
authored
utils/ulimit: ensure default/runtime is valid or log/error (#1232)
1 parent 86d9e6a commit b0aeecb

File tree

10 files changed

+189
-54
lines changed

10 files changed

+189
-54
lines changed

app/process/process.go

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/ava-labs/avalanchego/utils/dynamicip"
2020
"github.com/ava-labs/avalanchego/utils/logging"
2121
"github.com/ava-labs/avalanchego/utils/perms"
22+
"github.com/ava-labs/avalanchego/utils/ulimit"
2223
"github.com/ava-labs/avalanchego/version"
2324
)
2425

@@ -71,6 +72,12 @@ func (p *process) Start() error {
7172
return err
7273
}
7374

75+
// update fd limit
76+
fdLimit := p.config.FdLimit
77+
if err := ulimit.Set(fdLimit, log); err != nil {
78+
return err
79+
}
80+
7481
// start the db manager
7582
var dbManager manager.Manager
7683
switch p.config.DatabaseConfig.Name {

config/config.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import (
4343
"github.com/ava-labs/avalanchego/utils/profiler"
4444
"github.com/ava-labs/avalanchego/utils/storage"
4545
"github.com/ava-labs/avalanchego/utils/timer"
46-
"github.com/ava-labs/avalanchego/utils/ulimit"
4746
"github.com/ava-labs/avalanchego/vms"
4847
)
4948

@@ -1107,16 +1106,12 @@ func GetNodeConfig(v *viper.Viper, buildDir string) (node.Config, error) {
11071106
}
11081107

11091108
// File Descriptor Limit
1110-
fdLimit := v.GetUint64(FdLimitKey)
1111-
if err := ulimit.Set(fdLimit); err != nil {
1112-
return node.Config{}, fmt.Errorf("failed to set fd limit correctly due to: %w", err)
1113-
}
1109+
nodeConfig.FdLimit = v.GetUint64(FdLimitKey)
11141110

11151111
// Tx Fee
11161112
nodeConfig.TxFeeConfig = getTxFeeConfig(v, nodeConfig.NetworkID)
11171113

11181114
// Genesis Data
1119-
11201115
nodeConfig.GenesisBytes, nodeConfig.AvaxAssetID, err = getGenesisData(v, nodeConfig.NetworkID)
11211116
if err != nil {
11221117
return node.Config{}, fmt.Errorf("unable to load genesis file: %w", err)

node/config.go

+3
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ type Config struct {
169169
// Plugin directory
170170
PluginDir string `json:"pluginDir"`
171171

172+
// File Descriptor Limit
173+
FdLimit uint64 `json:"fdLimit"`
174+
172175
// Consensus configuration
173176
ConsensusParams avalanche.Parameters `json:"consensusParams"`
174177

utils/ulimit/ulimit.go

-9
This file was deleted.

utils/ulimit/ulimit_bsd.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
//go:build freebsd
5+
// +build freebsd
6+
7+
package ulimit
8+
9+
import (
10+
"fmt"
11+
"syscall"
12+
13+
"github.com/ava-labs/avalanchego/utils/logging"
14+
)
15+
16+
const DefaultFDLimit = 32 * 1024
17+
18+
// Set attempts to bump the Rlimit which has a soft (Cur) and a hard (Max) value.
19+
// The soft limit is what is used by the kernel to report EMFILE errors. The hard
20+
// limit is a secondary limit which the process can be bumped to without additional
21+
// privileges. Bumping the Max limit further would require superuser privileges.
22+
// If the value is below the recommendation warn on start.
23+
// see: http://0pointer.net/blog/file-descriptor-limits.html
24+
func Set(max uint64, log logging.Logger) error {
25+
// Note: BSD Rlimit is type int64
26+
// ref: https://cs.opensource.google/go/x/sys/+/b874c991:unix/ztypes_freebsd_amd64.go
27+
bsdMax := int64(max)
28+
var rLimit syscall.Rlimit
29+
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
30+
if err != nil {
31+
return fmt.Errorf("error getting rlimit: %w", err)
32+
}
33+
34+
if bsdMax > rLimit.Max {
35+
return fmt.Errorf("error fd-limit: (%d) greater than max: (%d)", limit, rLimit.Max)
36+
}
37+
38+
rLimit.Cur = bsdMax
39+
40+
// set new limit
41+
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
42+
return fmt.Errorf("error setting fd-limit: %w", err)
43+
}
44+
45+
// verify limit
46+
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
47+
return fmt.Errorf("error getting rlimit: %w", err)
48+
}
49+
50+
if rLimit.Cur < DefaultFDLimit {
51+
log.Warn("fd-limit: (%d) is less than recommended: (%d) and could result in reduced performance", rLimit.Cur, DefaultFDLimit)
52+
}
53+
54+
return nil
55+
}

utils/ulimit/ulimit_darwin.go

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
//go:build darwin
5+
// +build darwin
6+
7+
package ulimit
8+
9+
import (
10+
"fmt"
11+
"syscall"
12+
13+
"github.com/ava-labs/avalanchego/utils/logging"
14+
)
15+
16+
const DefaultFDLimit = 10 * 1024
17+
18+
// Set attempts to bump the Rlimit which has a soft (Cur) and a hard (Max) value.
19+
// The soft limit is what is used by the kernel to report EMFILE errors. The hard
20+
// limit is a secondary limit which the process can be bumped to without additional
21+
// privileges. Bumping the Max limit further would require superuser privileges.
22+
// If the value is below the recommendation warn on start.
23+
// see: http://0pointer.net/blog/file-descriptor-limits.html
24+
func Set(max uint64, log logging.Logger) error {
25+
var rLimit syscall.Rlimit
26+
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
27+
if err != nil {
28+
return fmt.Errorf("error getting rlimit: %w", err)
29+
}
30+
31+
// Darwin max number of FDs to allocatable for darwin systems.
32+
// The max file limit is 10240, even though the max returned by
33+
// Getrlimit is 1<<63-1. This is OPEN_MAX in sys/syslimits.h.
34+
// See https://github.com/golang/go/issues/30401
35+
if max > DefaultFDLimit {
36+
return fmt.Errorf("error fd-limit: (%d) greater than max: (%d)", max, DefaultFDLimit)
37+
}
38+
39+
rLimit.Cur = max
40+
41+
// set new limit
42+
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
43+
return fmt.Errorf("error setting fd-limit: %w", err)
44+
}
45+
46+
// verify limit
47+
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
48+
return fmt.Errorf("error getting rlimit: %w", err)
49+
}
50+
51+
if rLimit.Cur < DefaultFDLimit {
52+
log.Warn("fd-limit: (%d) is less than recommended: (%d) and could result in reduced performance", rLimit.Cur, DefaultFDLimit)
53+
}
54+
55+
return nil
56+
}

utils/ulimit/ulimit_non_unix.go.go

-10
This file was deleted.

utils/ulimit/ulimit_test.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
package ulimit
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
11+
"github.com/ava-labs/avalanchego/utils/logging"
12+
)
13+
14+
// Test_SetDefault performs sanity checks for the os default.
15+
func Test_SetDefault(t *testing.T) {
16+
assert := assert.New(t)
17+
err := Set(DefaultFDLimit, logging.NoLog{})
18+
assert.NoErrorf(err, "default fd-limit failed %v", err)
19+
}

utils/ulimit/ulimit_unix.go

+29-29
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,51 @@
1-
// This file was taken from:
2-
// https://github.com/OpenBazaar/openbazaar-go/blob/master/core/ulimit_non_unix.go
1+
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
33

4-
//go:build darwin || linux || netbsd || openbsd
5-
// +build darwin linux netbsd openbsd
4+
//go:build linux || netbsd || openbsd
5+
// +build linux netbsd openbsd
66

77
package ulimit
88

99
import (
1010
"fmt"
11-
"runtime"
1211
"syscall"
12+
13+
"github.com/ava-labs/avalanchego/utils/logging"
1314
)
1415

15-
// Set the file descriptor limit
16-
func Set(limit uint64) error {
16+
const DefaultFDLimit = 32 * 1024
17+
18+
// Set attempts to bump the Rlimit which has a soft (Cur) and a hard (Max) value.
19+
// The soft limit is what is used by the kernel to report EMFILE errors. The hard
20+
// limit is a secondary limit which the process can be bumped to without additional
21+
// privileges. Bumping the Max limit further would require superuser privileges.
22+
// If the current Max is below our recommendation we will warn on start.
23+
// see: http://0pointer.net/blog/file-descriptor-limits.html
24+
func Set(max uint64, log logging.Logger) error {
1725
var rLimit syscall.Rlimit
1826
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
1927
if err != nil {
2028
return fmt.Errorf("error getting rlimit: %w", err)
2129
}
2230

23-
oldMax := rLimit.Max
24-
if rLimit.Cur < limit {
25-
if rLimit.Max < limit {
26-
rLimit.Max = limit
27-
}
28-
rLimit.Cur = limit
31+
if max > rLimit.Max {
32+
return fmt.Errorf("error fd-limit: (%d) greater than max: (%d)", max, rLimit.Max)
2933
}
3034

31-
// If we're on darwin, work around the fact that Getrlimit reports the wrong
32-
// value. See https://github.com/golang/go/issues/30401
33-
if runtime.GOOS == "darwin" && rLimit.Cur > 10240 {
34-
// The max file limit is 10240, even though the max returned by
35-
// Getrlimit is 1<<63-1. This is OPEN_MAX in sys/syslimits.h.
36-
rLimit.Max = 10240
37-
rLimit.Cur = 10240
38-
}
35+
rLimit.Cur = max
3936

40-
// Try updating the limit. If it fails, try using the previous maximum
41-
// instead of our new maximum. Not all users have permissions to increase
42-
// the maximum.
37+
// set new limit
4338
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
44-
rLimit.Max = oldMax
45-
rLimit.Cur = oldMax
46-
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
47-
return fmt.Errorf("error setting ulimit: %w", err)
48-
}
39+
return fmt.Errorf("error setting fd-limit: %w", err)
40+
}
41+
42+
// verify limit
43+
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit); err != nil {
44+
return fmt.Errorf("error getting rlimit: %w", err)
45+
}
46+
47+
if rLimit.Cur < DefaultFDLimit {
48+
log.Warn("fd-limit: (%d) is less than recommended: (%d) and could result in reduced performance", rLimit.Cur, DefaultFDLimit)
4949
}
5050

5151
return nil

utils/ulimit/ulimit_windows.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
2+
// See the file LICENSE for licensing terms.
3+
4+
//go:build windows
5+
// +build windows
6+
7+
package ulimit
8+
9+
import "github.com/ava-labs/avalanchego/utils/logging"
10+
11+
const DefaultFDLimit = 16384
12+
13+
// Set is a no-op for windows and will warn if the default is not used.
14+
func Set(max uint64, log logging.Logger) error {
15+
if max != DefaultFDLimit {
16+
log.Warn("fd-limit is not supported for windows")
17+
}
18+
return nil
19+
}

0 commit comments

Comments
 (0)