Skip to content

Commit

Permalink
Fixes #1083 MaxFileDescriptors setting shouldn't reduce the system de…
Browse files Browse the repository at this point in the history
…fined values

#1083
  • Loading branch information
Traun Leyden committed Jan 22, 2016
1 parent a30f4e9 commit 06f294f
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 12 deletions.
96 changes: 84 additions & 12 deletions src/github.com/couchbase/sync_gateway/base/rlimit.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,97 @@ package base

import "syscall"

func SetMaxFileDescriptors(maxFDs uint64) (uint64, error) {
// Set Max File Descriptor limits
//
// Background information:
//
// - SG docs
// http://developer.couchbase.com/documentation/mobile/1.1.0/develop/guides/sync-gateway/os-level-tuning/max-file-descriptors/index.html
// - Related SG issues
// https://github.com/couchbase/sync_gateway/issues/1083
// - Hard limit vs Soft limit
// http://unix.stackexchange.com/questions/29577/ulimit-difference-between-hard-and-soft-limits
func SetMaxFileDescriptors(requestedSoftFDLimit uint64) (uint64, error) {

requiresUpdate, recommendedSoftFDLimit, err := getSoftFDLimit(requestedSoftFDLimit)
if err != nil {
return 0, err
}

// No call to Setrlimit required, because the requested soft limit is lower than current soft limit
if !requiresUpdate {
return 0, nil
}

// Get existing limits
var limits syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits); err != nil {
return maxFDs, err
return 0, err
}
if maxFDs > limits.Max {
maxFDs = limits.Max

// Update the soft limit (but don't bother updating the hard limit, since only root can do that,
// and it's assumed that this process is not running as root)
limits.Cur = recommendedSoftFDLimit
err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)

if err == nil {
Logf("Configured process to allow %d open file descriptors", recommendedSoftFDLimit)
}
if limits.Cur == maxFDs {
return maxFDs, nil

return recommendedSoftFDLimit, err

}

func getSoftFDLimit(requestedSoftFDLimit uint64) (requiresUpdate bool, recommendedSoftFDLimit uint64, err error) {

var limits syscall.Rlimit

if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits); err != nil {
return false, 0, err
}
limits.Cur = maxFDs
limits.Max = maxFDs

err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
requiresUpdate, recommendedSoftFDLimit = getSoftFDLimitWithCurrent(requestedSoftFDLimit, limits.Cur, limits.Max)

if err == nil {
Logf("Configured process to allow %d open file descriptors", maxFDs)
return requiresUpdate, recommendedSoftFDLimit, nil

}

// Get the recommended file descriptor settings
//
// Given:
//
// 1. The max file descriptors requested in the config (or default value)
// 2. The current ulimit settings
//
// return the recommended soft limit for the number of open file descriptors
// this process can have open.
//
// Rules:
//
// 1. Only return a value that is HIGHER than the existing soft limit, since
// a very small percentage of users will wanting to be lowering the limit
// based on this configuration.
// 1. Only return a value that is LESS-THAN-OR-EQUAL to the existing hard limit
// since trying to set something higher than the hard limit will fail
func getSoftFDLimitWithCurrent(requestedSoftFDLimit, currentSoftFdLimit, currentHardFdLimit uint64) (requiresUpdate bool, recommendedSoftFDLimit uint64) {

// Is the user requesting something that is less than the existing soft limit?
if requestedSoftFDLimit < currentSoftFdLimit {
// yep, and there is no point in doing so, so return false for requiresUpdate.
Logf("requestedSoftFDLimit < currentSoftFdLimit (%v < %v) no action needed", requestedSoftFDLimit, currentSoftFdLimit)
return false, currentSoftFdLimit
}

return maxFDs, err
// Is the user requesting something higher than the existing hard limit?
if requestedSoftFDLimit >= currentHardFdLimit {
// yes, so just use the hard limit
Logf("requestedSoftFDLimit >= currentHardFdLimit (%v >= %v) capping at %v", requestedSoftFDLimit, currentHardFdLimit, currentHardFdLimit)
return true, currentHardFdLimit
}

// The user is requesting something higher than the existing soft limit
// but lower than the existing hard limit, so allow this (and it will become
// the new soft limit once the Setrlimit call has been made)
return true, requestedSoftFDLimit

}
32 changes: 32 additions & 0 deletions src/github.com/couchbase/sync_gateway/base/rlimit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package base

import (
"testing"

"github.com/couchbaselabs/go.assert"
)

func TestGetSoftFDLimitWithCurrent(t *testing.T) {

requestedSoftFDLimit := uint64(1024)
currentSoftFdLimit := uint64(2048)
currentHardFdLimit := uint64(4096)

requiresUpdate, softFDLimit := getSoftFDLimitWithCurrent(
requestedSoftFDLimit,
currentSoftFdLimit,
currentHardFdLimit,
)
assert.False(t, requiresUpdate)

currentSoftFdLimit = uint64(512)

requiresUpdate, softFDLimit = getSoftFDLimitWithCurrent(
requestedSoftFDLimit,
currentSoftFdLimit,
currentHardFdLimit,
)
assert.True(t, requiresUpdate)
assert.Equals(t, softFDLimit, requestedSoftFDLimit)

}

0 comments on commit 06f294f

Please sign in to comment.