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  }