Merge branch '2-rlimit'
Use rlimit() to allow the server to have more than 1024 simultaneous open file descriptors. That would limit us to about 510 connections. The new limit varies depending on the operating system, but is closer to a million. closes #2
This commit is contained in:
		
						commit
						1e2520b41d
					
				
							
								
								
									
										35
									
								
								src/wormhole_transit_relay/increase_rlimits.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/wormhole_transit_relay/increase_rlimits.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
try:
 | 
			
		||||
    # 'resource' is unix-only
 | 
			
		||||
    from resource import getrlimit, setrlimit, RLIMIT_NOFILE
 | 
			
		||||
except ImportError: # pragma: nocover
 | 
			
		||||
    getrlimit, setrlimit, RLIMIT_NOFILE = None, None, None # pragma: nocover
 | 
			
		||||
from twisted.python import log
 | 
			
		||||
 | 
			
		||||
def increase_rlimits():
 | 
			
		||||
    if getrlimit is None:
 | 
			
		||||
        log.msg("unable to import 'resource', leaving rlimit alone")
 | 
			
		||||
        return
 | 
			
		||||
    soft, hard = getrlimit(RLIMIT_NOFILE)
 | 
			
		||||
    if soft >= 10000:
 | 
			
		||||
        log.msg("RLIMIT_NOFILE.soft was %d, leaving it alone" % soft)
 | 
			
		||||
        return
 | 
			
		||||
    # OS-X defaults to soft=7168, and reports a huge number for 'hard',
 | 
			
		||||
    # but won't accept anything more than soft=10240, so we can't just
 | 
			
		||||
    # set soft=hard. Linux returns (1024, 1048576) and is fine with
 | 
			
		||||
    # soft=hard. Cygwin is reported to return (256,-1) and accepts up to
 | 
			
		||||
    # soft=3200. So we try multiple values until something works.
 | 
			
		||||
    for newlimit in [hard, 10000, 3200, 1024]:
 | 
			
		||||
        log.msg("changing RLIMIT_NOFILE from (%s,%s) to (%s,%s)" %
 | 
			
		||||
                (soft, hard, newlimit, hard))
 | 
			
		||||
        try:
 | 
			
		||||
            setrlimit(RLIMIT_NOFILE, (newlimit, hard))
 | 
			
		||||
            log.msg("setrlimit successful")
 | 
			
		||||
            return
 | 
			
		||||
        except ValueError as e:
 | 
			
		||||
            log.msg("error during setrlimit: %s" % e)
 | 
			
		||||
            continue
 | 
			
		||||
        except:
 | 
			
		||||
            log.msg("other error during setrlimit, leaving it alone")
 | 
			
		||||
            log.err()
 | 
			
		||||
            return
 | 
			
		||||
    log.msg("unable to change rlimit, leaving it alone")
 | 
			
		||||
| 
						 | 
				
			
			@ -1,11 +1,12 @@
 | 
			
		|||
import os
 | 
			
		||||
from . import transit_server
 | 
			
		||||
from twisted.internet import reactor
 | 
			
		||||
from twisted.python import usage
 | 
			
		||||
from twisted.application.service import MultiService
 | 
			
		||||
from twisted.application.internet import (TimerService,
 | 
			
		||||
                                          StreamServerEndpointService)
 | 
			
		||||
from twisted.internet import endpoints
 | 
			
		||||
from . import transit_server
 | 
			
		||||
from .increase_rlimits import increase_rlimits
 | 
			
		||||
 | 
			
		||||
LONGDESC = """\
 | 
			
		||||
This plugin sets up a 'Transit Relay' server for magic-wormhole. This service
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +30,7 @@ class Options(usage.Options):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def makeService(config, reactor=reactor):
 | 
			
		||||
    increase_rlimits()
 | 
			
		||||
    ep = endpoints.serverFromString(reactor, config["port"]) # to listen
 | 
			
		||||
    log_file = (os.fdopen(int(config["log-fd"]), "w")
 | 
			
		||||
                if config["log-fd"] is not None
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										57
									
								
								src/wormhole_transit_relay/test/test_rlimits.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/wormhole_transit_relay/test/test_rlimits.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
from __future__ import print_function, unicode_literals
 | 
			
		||||
import mock
 | 
			
		||||
from twisted.trial import unittest
 | 
			
		||||
from ..increase_rlimits import increase_rlimits
 | 
			
		||||
 | 
			
		||||
class RLimits(unittest.TestCase):
 | 
			
		||||
    def test_rlimit(self):
 | 
			
		||||
        def patch_r(name, *args, **kwargs):
 | 
			
		||||
            return mock.patch("wormhole_transit_relay.increase_rlimits." + name, *args, **kwargs)
 | 
			
		||||
        fakelog = []
 | 
			
		||||
        def checklog(*expected):
 | 
			
		||||
            self.assertEqual(fakelog, list(expected))
 | 
			
		||||
            fakelog[:] = []
 | 
			
		||||
        NF = "NOFILE"
 | 
			
		||||
        mock_NF = patch_r("RLIMIT_NOFILE", NF)
 | 
			
		||||
 | 
			
		||||
        with patch_r("log.msg", fakelog.append):
 | 
			
		||||
            with patch_r("getrlimit", None):
 | 
			
		||||
                increase_rlimits()
 | 
			
		||||
            checklog("unable to import 'resource', leaving rlimit alone")
 | 
			
		||||
 | 
			
		||||
            with mock_NF:
 | 
			
		||||
                with patch_r("getrlimit", return_value=(20000, 30000)) as gr:
 | 
			
		||||
                    increase_rlimits()
 | 
			
		||||
                    self.assertEqual(gr.mock_calls, [mock.call(NF)])
 | 
			
		||||
                    checklog("RLIMIT_NOFILE.soft was 20000, leaving it alone")
 | 
			
		||||
 | 
			
		||||
                with patch_r("getrlimit", return_value=(10, 30000)) as gr:
 | 
			
		||||
                    with patch_r("setrlimit", side_effect=TypeError("other")):
 | 
			
		||||
                        with patch_r("log.err") as err:
 | 
			
		||||
                            increase_rlimits()
 | 
			
		||||
                        self.assertEqual(err.mock_calls, [mock.call()])
 | 
			
		||||
                        checklog("changing RLIMIT_NOFILE from (10,30000) to (30000,30000)",
 | 
			
		||||
                                 "other error during setrlimit, leaving it alone")
 | 
			
		||||
 | 
			
		||||
                    for maxlimit in [40000, 20000, 9000, 2000, 1000]:
 | 
			
		||||
                        def setrlimit(which, newlimit):
 | 
			
		||||
                            if newlimit[0] > maxlimit:
 | 
			
		||||
                                raise ValueError("nope")
 | 
			
		||||
                            return None
 | 
			
		||||
                        calls = []
 | 
			
		||||
                        expected = []
 | 
			
		||||
                        for tries in [30000, 10000, 3200, 1024]:
 | 
			
		||||
                            calls.append(mock.call(NF, (tries, 30000)))
 | 
			
		||||
                            expected.append("changing RLIMIT_NOFILE from (10,30000) to (%d,30000)" % tries)
 | 
			
		||||
                            if tries > maxlimit:
 | 
			
		||||
                                expected.append("error during setrlimit: nope")
 | 
			
		||||
                            else:
 | 
			
		||||
                                expected.append("setrlimit successful")
 | 
			
		||||
                                break
 | 
			
		||||
                        else:
 | 
			
		||||
                            expected.append("unable to change rlimit, leaving it alone")
 | 
			
		||||
 | 
			
		||||
                        with patch_r("setrlimit", side_effect=setrlimit) as sr:
 | 
			
		||||
                            increase_rlimits()
 | 
			
		||||
                        self.assertEqual(sr.mock_calls, calls)
 | 
			
		||||
                        checklog(*expected)
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user