changeset f49e2e7fc2e6 in trytond:default
details: https://hg.tryton.org/trytond?cmd=changeset;node=f49e2e7fc2e6
description:
Add coroutine concurrency option
issue8010
review53501002
diffstat:
CHANGELOG | 1 +
bin/trytond | 38 ++++++++++++++++++++++++---------
doc/topics/install.rst | 1 +
doc/topics/start_server.rst | 9 ++++++++
setup.py | 1 +
trytond/application.py | 4 +++
trytond/backend/postgresql/database.py | 6 +++++
trytond/commandline.py | 2 +
trytond/tools/gevent.py | 11 +++++++++
9 files changed, 62 insertions(+), 11 deletions(-)
diffs (180 lines):
diff -r 21c4acd056a7 -r f49e2e7fc2e6 CHANGELOG
--- a/CHANGELOG Mon Feb 04 22:46:32 2019 +0100
+++ b/CHANGELOG Tue Feb 05 19:09:53 2019 +0100
@@ -1,3 +1,4 @@
+* Add coroutine concurrency option
* Add increasing delay on database operational error retry
* Allows to lock records for update
* Remove _nocache on Transaction
diff -r 21c4acd056a7 -r f49e2e7fc2e6 bin/trytond
--- a/bin/trytond Mon Feb 04 22:46:32 2019 +0100
+++ b/bin/trytond Tue Feb 05 19:09:53 2019 +0100
@@ -1,11 +1,10 @@
#!/usr/bin/env python3
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
-import sys
+import glob
+import logging
import os
-import glob
-
-from werkzeug.serving import run_simple
+import sys
DIR = os.path.abspath(os.path.normpath(os.path.join(__file__,
'..', '..', 'trytond')))
@@ -20,6 +19,11 @@
commandline.config_log(options)
extra_files = config.update_etc(options.configfile)
+if options.coroutine:
+ # Monkey patching must be done before importing
+ from gevent import monkey
+ monkey.patch_all()
+
# Import trytond things after it is configured
from trytond.wsgi import app
from trytond.pool import Pool
@@ -33,15 +37,27 @@
certificate = config.get('ssl', 'certificate')
privatekey = config.get('ssl', 'privatekey')
if certificate or privatekey:
- ssl_context = (certificate, privatekey)
+ from werkzeug.serving import load_ssl_context
+ ssl_args = dict(
+ ssl_context=load_ssl_context(certificate, privatekey))
else:
- ssl_context = None
+ ssl_args = {}
for module in get_module_list():
info = get_module_info(module)
path = os.path.join(info['directory'], 'view', '*.xml')
extra_files.extend(glob.glob(path))
- run_simple(hostname, port, app,
- threaded=True,
- extra_files=extra_files,
- ssl_context=ssl_context,
- use_reloader=options.dev)
+
+ if options.coroutine:
+ from gevent.pywsgi import WSGIServer
+ logger = logging.getLogger('gevent')
+ WSGIServer((hostname, port), app,
+ log=logger,
+ error_log=logger,
+ **ssl_args).serve_forever()
+ else:
+ from werkzeug.serving import run_simple
+ run_simple(hostname, port, app,
+ threaded=True,
+ extra_files=extra_files,
+ use_reloader=options.dev,
+ **ssl_args)
diff -r 21c4acd056a7 -r f49e2e7fc2e6 doc/topics/install.rst
--- a/doc/topics/install.rst Mon Feb 04 22:46:32 2019 +0100
+++ b/doc/topics/install.rst Tue Feb 05 19:09:53 2019 +0100
@@ -26,6 +26,7 @@
(http://github.com/miohtama/python-Levenshtein)
* Optional: bcrypt (https://github.com/pyca/bcrypt)
* Optional: html2text (https://pypi.python.org/pypi/html2text)
+ * Optional: gevent (http://www.gevent.org/)
Install Tryton
==============
diff -r 21c4acd056a7 -r f49e2e7fc2e6 doc/topics/start_server.rst
--- a/doc/topics/start_server.rst Mon Feb 04 22:46:32 2019 +0100
+++ b/doc/topics/start_server.rst Tue Feb 05 19:09:53 2019 +0100
@@ -26,11 +26,20 @@
* `TRYTOND_CONFIG`: Point to :ref:`configuration <topics-configuration>` file.
* `TRYTOND_LOGGING_CONFIG`: Point to :ref:`logging <topics-logs>` file.
+ * `TRYTOND_COROUTINE`: Use coroutine for concurrency.
* `TRYTOND_DATABASE_NAMES`: A list of database names in CSV format, using
python default dialect.
.. warning:: You must manage to serve the static files from the web root.
+Coroutine server
+----------------
+
+The Werkzeug server uses thread for concurrency. This is not optimal for the
+long-polling request on the :ref:`bus <ref-bus>` as each client consumes
+permanently one thread.
+You can start the server with coroutine using the option `--coroutine`.
+
Cron service
============
diff -r 21c4acd056a7 -r f49e2e7fc2e6 setup.py
--- a/setup.py Mon Feb 04 22:46:32 2019 +0100
+++ b/setup.py Tue Feb 05 19:09:53 2019 +0100
@@ -138,6 +138,7 @@
'Levenshtein': ['python-Levenshtein'],
'BCrypt': ['passlib[bcrypt]'],
'html2text': ['html2text'],
+ 'coroutine': ['gevent>=1.1'],
},
zip_safe=False,
test_suite='trytond.tests',
diff -r 21c4acd056a7 -r f49e2e7fc2e6 trytond/application.py
--- a/trytond/application.py Mon Feb 04 22:46:32 2019 +0100
+++ b/trytond/application.py Tue Feb 05 19:09:53 2019 +0100
@@ -12,6 +12,10 @@
if logging_config:
logging.config.fileConfig(logging_config)
+if os.environ.get('TRYTOND_COROUTINE'):
+ from gevent import monkey
+ monkey.patch_all()
+
from trytond.pool import Pool
from trytond.wsgi import app
diff -r 21c4acd056a7 -r f49e2e7fc2e6 trytond/backend/postgresql/database.py
--- a/trytond/backend/postgresql/database.py Mon Feb 04 22:46:32 2019 +0100
+++ b/trytond/backend/postgresql/database.py Tue Feb 05 19:09:53 2019 +0100
@@ -36,6 +36,7 @@
from trytond.backend.database import DatabaseInterface, SQLType
from trytond.config import config, parse_uri
from trytond.protocols.jsonrpc import JSONDecoder
+from trytond.tools.gevent import is_gevent_monkey_patched
__all__ = ['Database', 'DatabaseIntegrityError', 'DatabaseOperationalError']
@@ -538,3 +539,8 @@
return json.loads(value, object_hook=JSONDecoder())
register_default_json(loads=convert_json)
register_default_jsonb(loads=convert_json)
+
+if is_gevent_monkey_patched():
+ from psycopg2.extensions import set_wait_callback
+ from psycopg2.extras import wait_select
+ set_wait_callback(wait_select)
diff -r 21c4acd056a7 -r f49e2e7fc2e6 trytond/commandline.py
--- a/trytond/commandline.py Mon Feb 04 22:46:32 2019 +0100
+++ b/trytond/commandline.py Tue Feb 05 19:09:53 2019 +0100
@@ -37,6 +37,8 @@
parser = get_parser()
parser.add_argument("--pidfile", dest="pidfile", metavar='FILE',
help="file where the server pid will be stored")
+ parser.add_argument("--coroutine", action="store_true", dest="coroutine",
+ help="use coroutine for concurrency")
return parser
diff -r 21c4acd056a7 -r f49e2e7fc2e6 trytond/tools/gevent.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/trytond/tools/gevent.py Tue Feb 05 19:09:53 2019 +0100
@@ -0,0 +1,11 @@
+# This file is part of Tryton. The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+
+
+def is_gevent_monkey_patched():
+ try:
+ from gevent import monkey
+ except ImportError:
+ return False
+ else:
+ return monkey.is_module_patched('__builtin__')