github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/server/config_unix.go (about) 1 // Copyright 2015 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // +build !windows 12 13 package server 14 15 import ( 16 "context" 17 "fmt" 18 19 "github.com/cockroachdb/cockroach/pkg/storage" 20 "github.com/cockroachdb/cockroach/pkg/util/log" 21 "github.com/cockroachdb/errors" 22 ) 23 24 // rlimit is a replacement struct for `unix.Rlimit` which abstracts 25 // from the possible differences in type definitions between platforms 26 // (e.g. GNU/Linux uses uint64, FreeBSD uses signed int64). 27 type rlimit struct { 28 Cur, Max uint64 29 } 30 31 func setOpenFileLimitInner(physicalStoreCount int) (uint64, error) { 32 minimumOpenFileLimit := uint64(physicalStoreCount*storage.MinimumMaxOpenFiles + minimumNetworkFileDescriptors) 33 networkConstrainedFileLimit := uint64(physicalStoreCount*storage.RecommendedMaxOpenFiles + minimumNetworkFileDescriptors) 34 recommendedOpenFileLimit := uint64(physicalStoreCount*storage.RecommendedMaxOpenFiles + recommendedNetworkFileDescriptors) 35 var rLimit rlimit 36 if err := getRlimitNoFile(&rLimit); err != nil { 37 if log.V(1) { 38 log.Infof(context.TODO(), "could not get rlimit; setting maxOpenFiles to the recommended value %d - %s", storage.RecommendedMaxOpenFiles, err) 39 } 40 return storage.RecommendedMaxOpenFiles, nil 41 } 42 43 // The max open file descriptor limit is too low. 44 if rLimit.Max < minimumOpenFileLimit { 45 return 0, fmt.Errorf("hard open file descriptor limit of %d is under the minimum required %d\n%s", 46 rLimit.Max, 47 minimumOpenFileLimit, 48 productionSettingsWebpage) 49 } 50 51 // If the current limit is less than the recommended limit, set the current 52 // limit to the minimum of the max limit or the recommendedOpenFileLimit. 53 var newCurrent uint64 54 if rLimit.Max > recommendedOpenFileLimit { 55 newCurrent = recommendedOpenFileLimit 56 } else { 57 newCurrent = rLimit.Max 58 } 59 if rLimit.Cur < newCurrent { 60 if log.V(1) { 61 log.Infof(context.TODO(), "setting the soft limit for open file descriptors from %d to %d", 62 rLimit.Cur, newCurrent) 63 } 64 oldCurrent := rLimit.Cur 65 rLimit.Cur = newCurrent 66 if err := setRlimitNoFile(&rLimit); err != nil { 67 // It is surprising if setrlimit fails, because we were careful to check 68 // getrlimit first to construct a valid limit. However, the validation 69 // rules for setrlimit have been known to change between Go versions (for 70 // an example, see https://github.com/golang/go/issues/30401), so we don't 71 // want to fail hard if setrlimit fails. Instead we log a warning and 72 // carry on. If the rlimit is really too low, we'll bail out later in this 73 // function. 74 log.Warningf(context.TODO(), "adjusting the limit for open file descriptors to %d failed: %s", 75 rLimit.Cur, err) 76 77 // Setting the limit to our "recommended" level failed. This may 78 // be because getRlimitNoFile gave us the wrong answer (on some 79 // platforms there are limits that are not reflected by 80 // getrlimit()). If the previous limit is below our minimum, try 81 // one more time to increase it to the minimum. 82 if oldCurrent < minimumOpenFileLimit { 83 rLimit.Cur = minimumOpenFileLimit 84 if err := setRlimitNoFile(&rLimit); err != nil { 85 log.Warningf(context.TODO(), "adjusting the limit for open file descriptors to %d failed: %s", 86 rLimit.Cur, err) 87 } 88 } 89 } 90 // Sadly, even when setrlimit returns successfully, the new limit is not 91 // always set as expected (e.g. on macOS), so fetch the limit again to see 92 // the actual current limit. 93 if err := getRlimitNoFile(&rLimit); err != nil { 94 return 0, errors.Wrap(err, "getting updated soft limit for open file descriptors") 95 } 96 if log.V(1) { 97 log.Infof(context.TODO(), "soft open file descriptor limit is now %d", rLimit.Cur) 98 } 99 } 100 101 // The current open file descriptor limit is still too low. 102 if rLimit.Cur < minimumOpenFileLimit { 103 return 0, fmt.Errorf("soft open file descriptor limit of %d is under the minimum required %d and cannot be increased\n%s", 104 rLimit.Cur, 105 minimumOpenFileLimit, 106 productionSettingsWebpage) 107 } 108 109 if rLimit.Cur < recommendedOpenFileLimit { 110 // We're still below the recommended amount, we should always show a 111 // warning. 112 log.Warningf(context.TODO(), "soft open file descriptor limit %d is under the recommended limit %d; this may decrease performance\n%s", 113 rLimit.Cur, 114 recommendedOpenFileLimit, 115 productionSettingsWebpage) 116 } 117 118 // If we have no physical stores, return 0. 119 if physicalStoreCount == 0 { 120 return 0, nil 121 } 122 123 // If the current open file descriptor limit meets or exceeds the recommended 124 // value, we can divide up the current limit, less what we need for 125 // networking, between the stores. 126 if rLimit.Cur >= recommendedOpenFileLimit { 127 return (rLimit.Cur - recommendedNetworkFileDescriptors) / uint64(physicalStoreCount), nil 128 } 129 130 // If we have more than enough file descriptors to hit the recommended number 131 // for each store, than only constrain the network ones by giving the stores 132 // their full recommended number. 133 if rLimit.Cur >= networkConstrainedFileLimit { 134 return storage.RecommendedMaxOpenFiles, nil 135 } 136 137 // Always sacrifice all but the minimum needed network descriptors to be 138 // used by the stores. 139 return (rLimit.Cur - minimumNetworkFileDescriptors) / uint64(physicalStoreCount), nil 140 }