diff options
author | Max Romanov <max.romanov@nginx.com> | 2018-01-29 16:17:36 +0300 |
---|---|---|
committer | Max Romanov <max.romanov@nginx.com> | 2018-01-29 16:17:36 +0300 |
commit | 9cd4fdbdb78e035254e8094b5cff2155857ab764 (patch) | |
tree | 6de4ee721996740cbeda8647e11b8b2aa18e704f /test | |
parent | a36babddef203d79dc37736661e1a042df4064f8 (diff) | |
download | unit-9cd4fdbdb78e035254e8094b5cff2155857ab764.tar.gz unit-9cd4fdbdb78e035254e8094b5cff2155857ab764.tar.bz2 |
Introducing extended app process management.
- Pre-fork 'processes.spare' application processes;
- fork more processes to keep 'processes.spare' idle processes;
- fork on-demand up to 'processes.max' count;
- scale down idle application processes above 'processes.spare' after
'processes.idle_timeout';
- number of concurrently started application processes also limited by
'processes.spare' (or 1, if spare is 0).
Diffstat (limited to '')
-rw-r--r-- | test/test_configuration.py | 30 | ||||
-rw-r--r-- | test/test_php_basic.py | 18 | ||||
-rw-r--r-- | test/test_python_application.py | 2 | ||||
-rw-r--r-- | test/test_python_atexit.py | 2 | ||||
-rw-r--r-- | test/test_python_basic.py | 18 | ||||
-rw-r--r-- | test/test_python_keepalive.py | 2 | ||||
-rw-r--r-- | test/test_python_procman.py | 237 |
7 files changed, 272 insertions, 37 deletions
diff --git a/test/test_configuration.py b/test/test_configuration.py index f7490069..700e4aa1 100644 --- a/test/test_configuration.py +++ b/test/test_configuration.py @@ -14,7 +14,7 @@ class TestUnitConfiguration(unit.TestUnitControl): { "ap\u0070": { "type": "\u0070ython", - "workers": 1, + "processes": { "spare": 0 }, "path": "\u002Fapp", "module": "wsgi" } @@ -25,7 +25,7 @@ class TestUnitConfiguration(unit.TestUnitControl): self.assertIn('success', self.conf({ "приложение": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } @@ -36,7 +36,7 @@ class TestUnitConfiguration(unit.TestUnitControl): { "app": { "type": "python", - "workers": \u0031, + "processes": { "spare": \u0030 }, "path": "/app", "module": "wsgi" } @@ -49,18 +49,16 @@ class TestUnitConfiguration(unit.TestUnitControl): def test_applications_string(self): self.assertIn('error', self.conf('"{}"', '/applications'), 'string') - @unittest.expectedFailure - def test_negative_workers(self): + def test_negative_spare(self): self.assertIn('error', self.conf({ "app": { "type": "python", - "workers": -1, + "processes": { "spare": -1 }, "path": "/app", "module": "wsgi" } - }, '/applications'), 'negative workers') + }, '/applications'), 'negative spare') - @unittest.expectedFailure def test_applications_type_only(self): self.assertIn('error', self.conf({ "app": { @@ -73,7 +71,7 @@ class TestUnitConfiguration(unit.TestUnitControl): { app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } @@ -85,7 +83,7 @@ class TestUnitConfiguration(unit.TestUnitControl): { "app" { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } @@ -97,7 +95,7 @@ class TestUnitConfiguration(unit.TestUnitControl): { "app": { "type": "python" - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } @@ -112,7 +110,7 @@ class TestUnitConfiguration(unit.TestUnitControl): self.assertIn('success', self.conf({ "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "../app", "module": "wsgi" } @@ -137,7 +135,7 @@ class TestUnitConfiguration(unit.TestUnitControl): "applications": { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } @@ -154,7 +152,7 @@ class TestUnitConfiguration(unit.TestUnitControl): "applications": { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } @@ -171,7 +169,7 @@ class TestUnitConfiguration(unit.TestUnitControl): "applications": { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } @@ -188,7 +186,7 @@ class TestUnitConfiguration(unit.TestUnitControl): "applications": { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } diff --git a/test/test_php_basic.py b/test/test_php_basic.py index dd757925..1487a9c5 100644 --- a/test/test_php_basic.py +++ b/test/test_php_basic.py @@ -9,7 +9,7 @@ class TestUnitBasic(unit.TestUnitControl): conf_app = { "app": { "type": "php", - "workers": 1, + "processes": { "spare": 0 }, "root": "/app", "index": "index.php" } @@ -34,7 +34,7 @@ class TestUnitBasic(unit.TestUnitControl): { "app": { "type": "php", - "workers": 1, + "processes": { "spare": 0 }, "root": "/app", "index": "index.php" } @@ -48,7 +48,7 @@ class TestUnitBasic(unit.TestUnitControl): { "app": { "type": "php", - "workers": 1, + "processes": { "spare": 0 }, "root": "/app", "index": "index.php" } @@ -61,7 +61,7 @@ class TestUnitBasic(unit.TestUnitControl): self.assertEqual(self.conf_get('/applications/app'), { "type": "php", - "workers": 1, + "processes": { "spare": 0 }, "root": "/app", "index": "index.php" }, @@ -72,8 +72,8 @@ class TestUnitBasic(unit.TestUnitControl): self.assertEqual(self.conf_get('/applications/app/type'), 'php', 'type') - self.assertEqual(self.conf_get('/applications/app/workers'), 1, - 'workers') + self.assertEqual(self.conf_get('/applications/app/processes/spare'), 0, + 'spare processes') def test_php_get_listeners(self): self.conf(self.conf_basic) @@ -118,9 +118,9 @@ class TestUnitBasic(unit.TestUnitControl): def test_php_change_application(self): self.conf(self.conf_basic) - self.conf('30', '/applications/app/workers') - self.assertEqual(self.conf_get('/applications/app/workers'), 30, - 'change application workers') + self.conf('30', '/applications/app/processes/max') + self.assertEqual(self.conf_get('/applications/app/processes/max'), 30, + 'change application max') self.conf('"/www"', '/applications/app/root') self.assertEqual(self.conf_get('/applications/app/root'), '/www', diff --git a/test/test_python_application.py b/test/test_python_application.py index 4fcedc43..1b99ea44 100644 --- a/test/test_python_application.py +++ b/test/test_python_application.py @@ -19,7 +19,7 @@ class TestUnitApplication(unit.TestUnitControl): "applications": { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": self.testdir + '/' + name, "module": "wsgi" } diff --git a/test/test_python_atexit.py b/test/test_python_atexit.py index 4dc984c1..4c8a6543 100644 --- a/test/test_python_atexit.py +++ b/test/test_python_atexit.py @@ -37,7 +37,7 @@ def application(env, start_response): "applications": { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": self.testdir + '/' + name, "module": "wsgi" } diff --git a/test/test_python_basic.py b/test/test_python_basic.py index ba63f4c6..ed0bd738 100644 --- a/test/test_python_basic.py +++ b/test/test_python_basic.py @@ -9,7 +9,7 @@ class TestUnitBasic(unit.TestUnitControl): conf_app = { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } @@ -44,7 +44,7 @@ class TestUnitBasic(unit.TestUnitControl): { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" } @@ -58,7 +58,7 @@ class TestUnitBasic(unit.TestUnitControl): { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module":"wsgi" } @@ -71,7 +71,7 @@ class TestUnitBasic(unit.TestUnitControl): self.assertEqual(self.conf_get('/applications/app'), { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": "/app", "module": "wsgi" }, @@ -82,8 +82,8 @@ class TestUnitBasic(unit.TestUnitControl): self.assertEqual(self.conf_get('/applications/app/type'), 'python', 'type') - self.assertEqual(self.conf_get('/applications/app/workers'), 1, - 'workers') + self.assertEqual(self.conf_get('/applications/app/processes/spare'), 0, + 'spare') def test_python_get_listeners(self): self.conf(self.conf_basic) @@ -128,9 +128,9 @@ class TestUnitBasic(unit.TestUnitControl): def test_python_change_application(self): self.conf(self.conf_basic) - self.conf('30', '/applications/app/workers') - self.assertEqual(self.conf_get('/applications/app/workers'), 30, - 'change application workers') + self.conf('30', '/applications/app/processes/max') + self.assertEqual(self.conf_get('/applications/app/processes/max'), 30, + 'change application max') self.conf('"/www"', '/applications/app/path') self.assertEqual(self.conf_get('/applications/app/path'), '/www', diff --git a/test/test_python_keepalive.py b/test/test_python_keepalive.py index 518befba..55607e9c 100644 --- a/test/test_python_keepalive.py +++ b/test/test_python_keepalive.py @@ -37,7 +37,7 @@ def application(environ, start_response): "applications": { "app": { "type": "python", - "workers": 1, + "processes": { "spare": 0 }, "path": self.testdir + '/' + name, "module": "wsgi" } diff --git a/test/test_python_procman.py b/test/test_python_procman.py new file mode 100644 index 00000000..5d2a5f50 --- /dev/null +++ b/test/test_python_procman.py @@ -0,0 +1,237 @@ +import os +import time +import unittest +import unit + +class TestUnitApplication(unit.TestUnitControl): + + def setUpClass(): + u = unit.TestUnit() + + u.check_modules('python') + u.check_version('0.3') + + def getWorkerCount(self): + n = 0 + for f in os.listdir(self.testdir): + if f.startswith('proctest.'): + n += 1 + + return n + + def getTestCode(self): + return """ +import atexit +import os + +fname = "%s.%%d" %% os.getpid() + +def remove_file(): + os.remove(fname) + +atexit.register(remove_file) + +open(fname, 'w') + +def application(env, start_response): + start_response('200 OK', [('Content-Type','text/html')]) + return [b'body'] + +""" % (self.testdir + '/proctest') + + + def test_python_prefork(self): + code, name = self.getTestCode(), 'py_app' + + self.python_application(name, code) + + self.conf({ + "listeners": { + "*:7080": { + "application": "app" + } + }, + "applications": { + "app": { + "type": "python", + "processes": 2, + "path": self.testdir + '/' + name, + "module": "wsgi" + } + } + }) + + self.assertEqual(self.getWorkerCount(), 2, 'python prefork 2 processes') + + self.get() + self.assertEqual(self.getWorkerCount(), 2, 'python prefork, still 2') + + self.conf('4', '/applications/app/processes') + + time.sleep(0.2) + + self.assertEqual(self.getWorkerCount(), 4, 'python prefork 4 processes') + + self.get() + self.assertEqual(self.getWorkerCount(), 4, 'python prefork, still 4') + + self.conf({ + "listeners": {}, + "applications": {} + }) + + time.sleep(0.2) + self.assertEqual(self.getWorkerCount(), 0, 'python stop all processes') + + time.sleep(2.2) + + + def test_python_ondemand(self): + code, name = self.getTestCode(), 'py_app' + + self.python_application(name, code) + + self.conf({ + "listeners": { + "*:7080": { + "application": "app" + } + }, + "applications": { + "app": { + "type": "python", + "processes": { + "spare": 0, + "max": 8, + "idle_timeout": 2 + }, + "path": self.testdir + '/' + name, + "module": "wsgi" + } + } + }) + + self.assertEqual(self.getWorkerCount(), 0, 'python on-demand') + + self.get() + self.assertEqual(self.getWorkerCount(), 1, 'python start on-demand') + + self.get() + self.assertEqual(self.getWorkerCount(), 1, 'python still 1') + + time.sleep(2.2) + self.assertEqual(self.getWorkerCount(), 0, 'python stop idle') + + self.conf({ + "listeners": {}, + "applications": {} + }) + + time.sleep(0.2) + self.assertEqual(self.getWorkerCount(), 0, 'python stop all processes') + + time.sleep(2.2) + + def test_python_scale_updown(self): + code, name = self.getTestCode(), 'py_app' + + self.python_application(name, code) + + self.conf({ + "listeners": { + "*:7080": { + "application": "app" + } + }, + "applications": { + "app": { + "type": "python", + "processes": { + "spare": 2, + "max": 8, + "idle_timeout": 2 + }, + "path": self.testdir + '/' + name, + "module": "wsgi" + } + } + }) + + self.assertEqual(self.getWorkerCount(), 2, 'python prefork 2') + + self.get() + time.sleep(0.2) + self.assertEqual(self.getWorkerCount(), 3, 'python keep 2 idle, 1 busy') + + self.get() + time.sleep(0.2) + self.assertEqual(self.getWorkerCount(), 3, 'python still 3') + + time.sleep(2.2) + self.assertEqual(self.getWorkerCount(), 2, 'python stop idle') + + self.get() + + time.sleep(0.5) + self.assertEqual(self.getWorkerCount(), 3, 'python keep 2 idle, 1 busy') + + self.conf({ + "listeners": {}, + "applications": {} + }) + + time.sleep(0.2) + self.assertEqual(self.getWorkerCount(), 0, 'python stop all processes') + + time.sleep(2.2) + + def test_python_reconfigure(self): + code, name = self.getTestCode(), 'py_app' + + self.python_application(name, code) + + self.conf({ + "listeners": { + "*:7080": { + "application": "app" + } + }, + "applications": { + "app": { + "type": "python", + "processes": { + "spare": 2, + "max": 6, + "idle_timeout": 2 + }, + "path": self.testdir + '/' + name, + "module": "wsgi" + } + } + }) + + self.assertEqual(self.getWorkerCount(), 2, 'python prefork 2') + + self.get() + time.sleep(0.2) + self.assertEqual(self.getWorkerCount(), 3, 'python keep 2 idle, 1 busy') + + self.conf('6', '/applications/app/processes/spare') + self.assertEqual(self.getWorkerCount(), 6, 'python prefork 6') + + self.get() + time.sleep(0.2) + self.assertEqual(self.getWorkerCount(), 6, 'python still 6') + + self.conf({ + "listeners": {}, + "applications": {} + }) + + time.sleep(0.2) + self.assertEqual(self.getWorkerCount(), 0, 'python stop all processes') + + time.sleep(2.2) + +if __name__ == '__main__': + unittest.main() |