Kaynağa Gözat

[Python] Flask: unifying application and fix nginx (#8139)

Oleg S 2 yıl önce
ebeveyn
işleme
dc144078c8

+ 6 - 11
frameworks/Python/flask/README.md

@@ -13,7 +13,7 @@ All test implementations are located within a single file
 
 ## Description
 
-Flask + Flask-Pony
+Flask and Flask with Pony
 
 ### Database
 
@@ -23,24 +23,19 @@ PostgresQL (psycopg2 on CPython, psycopg2cffi on PyPy)
 
 * gunicorn+meinheld on CPython
 * gunicorn on PyPy
+* fastwsgi on CPython
+* socketify on CPython
 
 ## Test URLs
+
 ### JSON Encoding 
 
 http://localhost:8080/json
 
 ### Single Row Random Query
 
-With ORM (app.py):
-    http://localhost:8080/db
-
-Without ORM (app_raw.py):
-    http://localhost:8080/db
+http://localhost:8080/db
 
 ### Variable Row Query Test 
 
-With ORM (app.py):
-    http://localhost:8080/query?queries=2
-
-Without ORM (app_raw.py):
-    http://localhost:8080/query?queries=2
+http://localhost:8080/query?queries=2

+ 0 - 50
frameworks/Python/flask/app-fastwsgi.py

@@ -1,50 +0,0 @@
-#!/usr/bin/env python
-from flask import Flask, make_response, jsonify
-import os
-import multiprocessing
-import logging
-import fastwsgi
-
-# setup
-app = Flask(__name__)
-app.config["JSONIFY_PRETTYPRINT_REGULAR"] = False
-
-
[email protected]("/json")
-def hello():
-    response = make_response(jsonify(message="Hello, World!"))
-    response.content_type = "application/json"
-    response.headers.set('Server', 'FastWSGI+Flask')
-    return response
-
[email protected]("/plaintext")
-def plaintext():
-    response = make_response(b"Hello, World!")
-    response.content_type = "text/plain"
-    response.headers.set('Server', 'FastWSGI+Flask')
-    return response
-
-
-_is_travis = os.environ.get('TRAVIS') == 'true'
-
-workers = int(multiprocessing.cpu_count())
-if _is_travis:
-    workers = 2
-
-host = '0.0.0.0'
-port = 8080
-
-def run_app():
-    fastwsgi.run(app, host, port, loglevel=0)
-
-def create_fork():
-    n = os.fork()
-    # n greater than 0 means parent process
-    if not n > 0:
-        run_app()
-
-# fork limiting the cpu count - 1
-for i in range(1, workers):
-    create_fork()
-
-run_app()  # run app on the main process too :)

+ 0 - 47
frameworks/Python/flask/app-socketify-wsgi.py

@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-from flask import Flask, make_response, jsonify
-import os
-import multiprocessing
-import logging
-from socketify import WSGI
-# setup
-app = Flask(__name__)
-app.config["JSONIFY_PRETTYPRINT_REGULAR"] = False
-
-
[email protected]("/json")
-def hello():
-    return jsonify(message="Hello, World!")
-
[email protected]("/plaintext")
-def plaintext():
-    """Test 6: Plaintext"""
-    response = make_response(b"Hello, World!")
-    response.content_type = "text/plain"
-    return response
-
-
-
-_is_travis = os.environ.get('TRAVIS') == 'true'
-
-workers = int(multiprocessing.cpu_count())
-if _is_travis:
-    workers = 2
-
-
-def run_app():
-    WSGI(app).listen(8080, lambda config: logging.info(f"Listening on http://localhost:{config.port} now\n")).run()
-
-
-def create_fork():
-    n = os.fork()
-    # n greater than 0 means parent process
-    if not n > 0:
-        run_app()
-
-
-# fork limiting the cpu count - 1
-for i in range(1, multiprocessing.cpu_count()):
-    create_fork()
-
-run_app()  # run app on the main process too :)

+ 232 - 53
frameworks/Python/flask/app.py

@@ -1,65 +1,78 @@
 #!/usr/bin/env python
+import os
+import sys
+import multiprocessing
+import itertools
 from collections import namedtuple
-from operator import attrgetter
+from operator import attrgetter, itemgetter
 from random import randint
-import sys
+from email.utils import formatdate
 
-from flask import Flask, request, render_template, make_response, jsonify
+import flask
 from pony import orm
 
+
 if sys.version_info[0] == 3:
     xrange = range
+    
 _is_pypy = hasattr(sys, "pypy_version_info")
 if _is_pypy:
-    from psycopg2cffi import compat
+    import psycopg2cffi.compat
+    psycopg2cffi.compat.register()
 
-    compat.register()
+_is_travis = os.environ.get('TRAVIS') == 'true'
 
-DBDRIVER = "postgres"
-DBHOST = "tfb-database"
+_is_gunicorn = "gunicorn" in os.environ.get("SERVER_SOFTWARE", "")
 
-# setup
+_cpu_count = multiprocessing.cpu_count()
+if _is_travis:
+    _cpu_count = 2
 
-app = Flask(__name__)
-app.config[
-    "STORM_DATABASE_URI"
-] = "{DBDRIVER}://benchmarkdbuser:benchmarkdbpass@{DBHOST}:5432/hello_world".format(
-    DBDRIVER=DBDRIVER, DBHOST=DBHOST
-)
-app.config["JSONIFY_PRETTYPRINT_REGULAR"] = False
-db = orm.Database()
-db.bind(
-    DBDRIVER,
-    host=DBHOST,
-    port=5432,
-    user="benchmarkdbuser",
-    password="benchmarkdbpass",
-    database="hello_world",
-)
+_raw_orm = os.getenv('USE_RAW', "0") == "1"
 
+_use_orjson = os.getenv('USE_ORJSON', "0") == "1"
+if _use_orjson:
+    import orjson as json
 
-class World(db.Entity):
-    _table_ = "world"
-    id = orm.PrimaryKey(int)
-    randomNumber = orm.Required(int, column="randomnumber")
+_use_ujson = os.getenv('USE_UJSON', "0") == "1"
+if _use_ujson:
+    import ujson as json
+    
+if not _use_orjson and not _use_ujson:
+    import json
+    from flask import jsonify
 
-    def to_dict(self):
-        """Return object data in easily serializeable format"""
-        return {"id": self.id, "randomNumber": self.randomNumber}
+DBDRV  = "postgres"
+DBHOST = "tfb-database"
+DBUSER = "benchmarkdbuser"
+DBPSWD = "benchmarkdbpass"
 
+# setup
 
-class Fortune(db.Entity):
-    _table_ = "fortune"
-    id = orm.PrimaryKey(int, auto=True)
-    message = orm.Required(str)
+app = flask.Flask(__name__)
+app.config["JSONIFY_PRETTYPRINT_REGULAR"] = False
 
+# -----------------------------------------------------------------------------
+response_server = None
+response_add_date = False
+
[email protected]_request
+def after_request(response):
+    if response_server:
+        response.headers['Server'] = response_server
+    if response_add_date:
+        response.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
+    return response
 
-db.generate_mapping(create_tables=False)
+if _use_orjson or _use_ujson:
+    def jsonify(jdict):
+        return json.dumps(jdict), { "Content-Type": "application/json" }
 
+# -----------------------------------------------------------------------------
 
 def get_num_queries():
     try:
-        num_queries = request.args.get("queries", 1, type=int)
+        num_queries = flask.request.args.get("queries", 1, type=int)
     except ValueError:
         num_queries = 1
     if num_queries < 1:
@@ -68,7 +81,6 @@ def get_num_queries():
         return 500
     return num_queries
 
-
 def generate_ids(num_queries):
     ids = {randint(1, 10000) for _ in xrange(num_queries)}
     while len(ids) < num_queries:
@@ -76,13 +88,99 @@ def generate_ids(num_queries):
     return list(sorted(ids))
 
 
+if _raw_orm:
+    import jinja2
+    
+    if _is_pypy:
+        from psycopg2cffi.pool import ThreadedConnectionPool
+        from psycopg2cffi.extras import execute_batch
+    else:
+        from psycopg2.pool import ThreadedConnectionPool
+        from psycopg2.extras import execute_batch
+    
+    pool_size = _cpu_count * 2.5
+    
+    POOL = ThreadedConnectionPool(
+        minconn=int(pool_size / 4),
+        maxconn=int(pool_size / 4),
+        database="hello_world",
+        user=DBUSER,
+        password=DBPSWD,
+        host="tfb-database",
+        port=5432,
+    )
+    read_row_sql = (
+        'SELECT world."randomnumber", world."id" FROM "world" WHERE id = %(id)s'
+    )
+    prepared_read_row_sql = (
+        'SELECT world."randomnumber", world."id" FROM "world" WHERE id = $1'
+    )
+    write_row_sql = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2'
+    ADDITIONAL_ROW = (0, "Additional fortune added at request time.")
+    db = POOL.getconn()
+    cursor = db.cursor()
+    cursor.execute("PREPARE read_stmt (int) AS " + prepared_read_row_sql)
+    cursor.execute("PREPARE write_stmt (int, int) AS " + write_row_sql)
+    cursor.execute('PREPARE fortune AS SELECT * FROM "Fortune"')
+    del cursor
+    POOL.putconn(db)
+    del db
+
+    def db_query(arg):
+        (cursor, ident) = arg
+        cursor.execute("EXECUTE read_stmt(%s)", [ident])
+        result = cursor.fetchone()
+        return result
+        
+    fn = os.path.join("templates", "fortune_raw.html")
+    with open(fn, "r") as template_file:
+        template_text = template_file.read()
+    FORTUNE_TEMPLATE = jinja2.Template(template_text)
+    #Fortune = namedtuple("Fortune", ["id", "message"])
+
+else: # --------- PonyORM ------------------------------------------------
+    app.config["STORM_DATABASE_URI"] = "{}://{}:{}@{}:5432/hello_world".format(DBDRV, DBUSER, DBPSWD, DBHOST)
+    
+    db = orm.Database()
+    db.bind(DBDRV, host=DBHOST, port=5432, user=DBUSER, password=DBPSWD, database="hello_world")
+
+    class World(db.Entity):
+        _table_ = "world"
+        id = orm.PrimaryKey(int)
+        randomNumber = orm.Required(int, column="randomnumber")
+
+        def to_dict(self):
+            """Return object data in easily serializeable format"""
+            return {"id": self.id, "randomNumber": self.randomNumber}
+
+    class Fortune(db.Entity):
+        _table_ = "fortune"
+        id = orm.PrimaryKey(int, auto=True)
+        message = orm.Required(str)
+
+    db.generate_mapping(create_tables=False)
+    
+# ----------------------------------------------------------------------------------------
+
 @app.route("/json")
 def hello():
-    return jsonify(message="Hello, World!")
+    if _use_orjson or _use_ujson:
+        return jsonify( {"message": "Hello, World!"} )
+
+    return flask.jsonify(message="Hello, World!")
 
 
 @app.route("/query")
 def get_random_world():
+    if _raw_orm:
+        db = POOL.getconn()
+        cursor = db.cursor()
+        num_queries = get_num_queries()
+        results = map(db_query, zip(itertools.repeat(cursor, num_queries), generate_ids(num_queries)))
+        worlds = [ {"id": result[0], "randomNumber": result[1]} for result in results ]
+        POOL.putconn(db)
+        return jsonify(worlds)
+    
     with orm.db_session(serializable=False):
         worlds = [World[ident].to_dict() for ident in generate_ids(get_num_queries())]
     return jsonify(worlds)
@@ -90,6 +188,15 @@ def get_random_world():
 
 @app.route("/db")
 def get_random_world_single():
+    if _raw_orm:
+        db = POOL.getconn()
+        cursor = db.cursor()
+        cursor.execute("EXECUTE read_stmt(%s)", generate_ids(1))
+        result = cursor.fetchone()
+        world = {"id": result[0], "randomNumber": result[1]}
+        POOL.putconn(db)
+        return jsonify(world)
+    
     wid = randint(1, 10000)
     with orm.db_session(serializable=False):
         world = World[wid]
@@ -98,6 +205,16 @@ def get_random_world_single():
 
 @app.route("/fortunes")
 def get_fortunes():
+    if _raw_orm:
+        db = POOL.getconn()
+        cursor = db.cursor()
+        cursor.execute("EXECUTE fortune")
+        fortunes = list(cursor.fetchall())
+        fortunes.append(ADDITIONAL_ROW)
+        fortunes.sort(key=itemgetter(1))
+        POOL.putconn(db)
+        return flask.Response(FORTUNE_TEMPLATE.render(fortunes=fortunes))
+    
     with orm.db_session(serializable=False):
         fortunes = list(orm.select(fortune for fortune in Fortune))
     tmp_fortune = namedtuple("Fortune", ["id", "message"])
@@ -105,12 +222,25 @@ def get_fortunes():
         tmp_fortune(id=0, message="Additional fortune added at request time.")
     )
     fortunes.sort(key=attrgetter("message"))
-    return render_template("fortunes.html", fortunes=fortunes)
+    return flask.render_template("fortunes.html", fortunes=fortunes)
 
 
 @app.route("/updates")
 def updates():
-    """Test 5: Database Updates"""
+    if _raw_orm:
+        db = POOL.getconn()
+        cursor = db.cursor()
+        num_queries = get_num_queries()
+        ids = generate_ids(num_queries)
+        update_values = generate_ids(num_queries)
+        list(map(db_query, zip(itertools.repeat(cursor, num_queries), generate_ids(num_queries))))
+        worlds = list(zip(ids, update_values))
+        execute_batch(cursor, "EXECUTE write_stmt(%s, %s)", worlds)
+        db.commit()
+        POOL.putconn(db)
+        data = [ {"id": ident, "randomNumber": update} for ident, update in worlds ]
+        return jsonify(data)
+
     num_queries = get_num_queries()
     ids = generate_ids(num_queries)
     ids.sort()
@@ -125,20 +255,69 @@ def updates():
 
 @app.route("/plaintext")
 def plaintext():
-    """Test 6: Plaintext"""
-    response = make_response(b"Hello, World!")
+    response = flask.make_response(b"Hello, World!")
     response.content_type = "text/plain"
     return response
 
+# -----------------------------------------------------------------------------------
 
-try:
-    import meinheld
-
-    meinheld.server.set_access_logger(None)
-    meinheld.set_keepalive(120)
-except ImportError:
-    pass
-
-# entry point for debugging
 if __name__ == "__main__":
-    app.run(debug=True)
+    import optparse
+    import logging
+    import re
+
+    parser = optparse.OptionParser("usage: %prog [options]", add_help_option=False)
+    parser.add_option("-h", "--host", dest="host", default='0.0.0.0', type="string")
+    parser.add_option("-p", "--port", dest="port", default=8080, type="int")
+    parser.add_option("-s", "--server", dest="server", default="gunicorn", type="string")
+    parser.add_option("-w", "--workers", dest="workers", default=0, type="int")
+    parser.add_option("-k", "--keepalive", dest="keepalive", default=60, type="int")
+    parser.add_option("-v", "--verbose", dest="verbose", default=0, type="int")
+    (opt, args) = parser.parse_args() 
+
+    workers = _cpu_count
+    if workers > 0:
+        workers = opt.workers
+
+    if _is_travis:
+        workers = 2
+
+    def run_app():
+        global response_server
+        global response_add_date
+        
+        if opt.server == "werkzeug":
+            import werkzeug
+            werkzeug.serving.WSGIRequestHandler.protocol_version = "HTTP/1.1"
+            wzlog = logging.getLogger("werkzeug")
+            wzlog.setLevel(logging.WARN)
+            use_reloader = False # True = Use a reloader process to restart the server process when files are changed
+            response_server = None
+            response_add_date = False
+            werkzeug.serving.run_simple(opt.host, opt.port, app, use_reloader=use_reloader) 
+
+        if opt.server == 'fastwsgi':
+            import fastwsgi
+            response_server = "FastWSGI"
+            response_add_date = False
+            fastwsgi.run(app, host=opt.host, port=opt.port, loglevel=opt.verbose)
+
+        if opt.server == 'socketify':
+            import socketify
+            response_server = None
+            response_add_date = False
+            msg = "Listening on http://0.0.0.0:{port} now\n".format(port=opt.port)
+            socketify.WSGI(app).listen(opt.port, lambda config: logging.info(msg)).run()
+
+    def create_fork():
+        n = os.fork()
+        # n greater than 0 means parent process
+        if not n > 0:
+            run_app()
+
+    # fork limiting the cpu count - 1
+    for i in range(1, workers):
+        create_fork()
+
+    run_app()  # run app on the main process too :)
+

+ 0 - 92
frameworks/Python/flask/app_raw.py

@@ -1,92 +0,0 @@
-#!/usr/bin/env python
-import multiprocessing
-from itertools import repeat
-from operator import itemgetter
-
-from flask import Flask, Response
-
-from helpers import *
-
-# setup
-
-
-
-app = Flask(__name__)
-(
-    POOL,
-    TEMPLATE,
-    READ_ROW_SQL,
-    PREPARED_READ_ROW_SQL,
-    WRITE_ROW_SQL,
-    ADDITIONAL_ROW,
-) = setup(multiprocessing.cpu_count() * 2.5)
-
-
[email protected]("/json")
-def hello():
-    return orjson.dumps({"message": "Hello, World!"}), {
-        "Content-Type": "application/json"
-    }
-
-
[email protected]("/query")
-def get_random_world():
-    db = POOL.getconn()
-    cursor = db.cursor()
-    num_queries = get_num_queries()
-    results = map(query, zip(repeat(cursor, num_queries), generate_ids(num_queries)))
-    worlds = [{"id": result[0], "randomNumber": result[1]} for result in results]
-    POOL.putconn(db)
-    return orjson.dumps(worlds), {"Content-Type": "application/json"}
-
-
[email protected]("/db")
-def get_random_world_single():
-    db = POOL.getconn()
-    cursor = db.cursor()
-    cursor.execute("EXECUTE read_stmt(%s)", generate_ids(1))
-    result = cursor.fetchone()
-    world = {"id": result[0], "randomNumber": result[1]}
-    POOL.putconn(db)
-    return orjson.dumps(world), {"Content-Type": "application/json"}
-
-
[email protected]("/fortunes")
-def get_fortunes():
-    db = POOL.getconn()
-    cursor = db.cursor()
-    cursor.execute("EXECUTE fortune")
-    fortunes = list(cursor.fetchall())
-    fortunes.append(ADDITIONAL_ROW)
-    fortunes.sort(key=itemgetter(1))
-    POOL.putconn(db)
-    return Response(TEMPLATE.render(fortunes=fortunes))
-
-
[email protected]("/updates")
-def updates():
-    """Test 5: Database Updates"""
-    db = POOL.getconn()
-    cursor = db.cursor()
-    num_queries = get_num_queries()
-    ids = generate_ids(num_queries)
-    update_values = generate_ids(num_queries)
-    list(map(query, zip(repeat(cursor, num_queries), generate_ids(num_queries))))
-    worlds = list(zip(ids, update_values))
-    execute_batch(cursor, "EXECUTE write_stmt(%s, %s)", worlds)
-    db.commit()
-    POOL.putconn(db)
-    return orjson.dumps(
-        [{"id": ident, "randomNumber": update} for ident, update in worlds]
-    ), {"Content-Type": "application/json"}
-
-
[email protected]("/plaintext")
-def plaintext():
-    """Test 6: Plaintext"""
-    return b"Hello, World!", {"Content-Type": "text/plain"}
-
-
-# entry point for debugging
-if __name__ == "__main__":
-    app.run(debug=True)

+ 25 - 21
frameworks/Python/flask/benchmark_config.json

@@ -16,11 +16,11 @@
       "language": "Python",
       "flavor": "Python3",
       "orm": "Full",
-      "platform": "Meinheld",
-      "webserver": "None",
+      "platform": "WSGI",
+      "webserver": "Meinheld",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Flask",
+      "display_name": "Flask [Meinheld]",
       "notes": "",
       "versus": "wsgi",
       "tags": [ ]
@@ -40,11 +40,11 @@
       "language": "Python",
       "flavor": "CPython",
       "orm": "Raw",
-      "platform": "Meinheld",
-      "webserver": "None",
+      "platform": "WSGI",
+      "webserver": "Meinheld",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Flask-raw",
+      "display_name": "Flask [Meinheld] [Raw]",
       "notes": "",
       "versus": "wsgi",
       "tags": [ ]
@@ -59,12 +59,12 @@
       "framework": "flask",
       "language": "Python",
       "flavor": "Python3",
-      "orm": "Full",
-      "platform": "None",
+      "orm": "Raw",
+      "platform": "WSGI",
       "webserver": "Socketify.py",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Flask [socketify.py WSGI]",
+      "display_name": "Flask [socketify.py]",
       "notes": "",
       "versus": "wsgi"
     },
@@ -78,12 +78,12 @@
       "framework": "flask",
       "language": "Python",
       "flavor": "PyPy",
-      "orm": "Full",
-      "platform": "None",
+      "orm": "Raw",
+      "platform": "WSGI",
       "webserver": "Socketify.py",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Flask [socketify.py WSGI PyPy3]",
+      "display_name": "Flask [socketify.py] [PyPy3]",
       "notes": "PyPy",
       "versus": "wsgi"
     },
@@ -102,11 +102,11 @@
       "language": "Python",
       "flavor": "PyPy",
       "orm": "Full",
-      "platform": "None",
+      "platform": "WSGI",
       "webserver": "Meinheld",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Flask",
+      "display_name": "Flask [Meinheld] [PyPy3]",
       "notes": "PyPy",
       "versus": "wsgi",
       "tags": [ ]
@@ -126,11 +126,11 @@
       "language": "Python",
       "flavor": "PyPy",
       "orm": "Raw",
-      "platform": "Meinheld",
-      "webserver": "None",
+      "platform": "WSGI",
+      "webserver": "Meinheld",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Flask-raw",
+      "display_name": "Flask [Meinheld] [PyPy3] [Raw]",
       "notes": "PyPy",
       "versus": "wsgi",
       "tags": [ ]
@@ -150,17 +150,21 @@
       "language": "Python",
       "flavor": "CPython",
       "orm": "Full",
-      "platform": "None",
+      "platform": "WSGI",
       "webserver": "nginx",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Flask",
+      "display_name": "Flask [nginx] [uWSGI]",
       "notes": "",
       "versus": "wsgi",
       "tags": [ ]
     },
     "fastwsgi": {
       "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "approach": "Realistic",
@@ -169,8 +173,8 @@
       "framework": "flask",
       "language": "Python",
       "flavor": "Python3",
-      "orm": "Full",
-      "platform": "None",
+      "orm": "Raw",
+      "platform": "WSGI",
       "webserver": "FastWSGI",
       "os": "Linux",
       "database_os": "Linux",

+ 19 - 15
frameworks/Python/flask/config.toml

@@ -14,8 +14,8 @@ database = "Postgres"
 database_os = "Linux"
 os = "Linux"
 orm = "Full"
-platform = "Meinheld"
-webserver = "None"
+platform = "WSGI"
+webserver = "Meinheld"
 versus = "wsgi"
 
 [raw]
@@ -31,8 +31,8 @@ database = "Postgres"
 database_os = "Linux"
 os = "Linux"
 orm = "Raw"
-platform = "Meinheld"
-webserver = "None"
+platform = "WSGI"
+webserver = "Meinheld"
 versus = "wsgi"
 
 [nginx-uwsgi]
@@ -48,7 +48,7 @@ database = "Postgres"
 database_os = "Linux"
 os = "Linux"
 orm = "Full"
-platform = "Meinheld"
+platform = "WSGI"
 webserver = "nginx"
 versus = "wsgi"
 
@@ -65,8 +65,8 @@ database = "Postgres"
 database_os = "Linux"
 os = "Linux"
 orm = "Raw"
-platform = "Meinheld"
-webserver = "None"
+platform = "WSGI"
+webserver = "Meinheld"
 versus = "wsgi"
 
 [socketify-wsgi]
@@ -77,8 +77,8 @@ classification = "Micro"
 database = "Postgres"
 database_os = "Linux"
 os = "Linux"
-orm = "Full"
-platform = "None"
+orm = "Raw"
+platform = "WSGI"
 webserver = "Socketify.py"
 versus = "wsgi"
 
@@ -90,8 +90,8 @@ classification = "Micro"
 database = "Postgres"
 database_os = "Linux"
 os = "Linux"
-orm = "Full"
-platform = "None"
+orm = "Raw"
+platform = "WSGI"
 webserver = "Socketify.py"
 versus = "wsgi"
 
@@ -108,19 +108,23 @@ database = "Postgres"
 database_os = "Linux"
 os = "Linux"
 orm = "Full"
-platform = "Meinheld"
-webserver = "None"
+platform = "WSGI"
+webserver = "Meinheld"
 versus = "wsgi"
 
 [fastwsgi]
 urls.plaintext = "/plaintext"
 urls.json = "/json"
+urls.db = "/db"
+urls.query = "/queries?queries="
+urls.update = "/updates?queries="
+urls.fortune = "/fortunes"
 approach = "Realistic"
 classification = "Micro"
 database = "Postgres"
 database_os = "Linux"
 os = "Linux"
-orm = "Full"
-platform = "None"
+orm = "Raw"
+platform = "WSGI"
 webserver = "FastWSGI"
 versus = "wsgi"

+ 4 - 1
frameworks/Python/flask/flask-fastwsgi.dockerfile

@@ -10,4 +10,7 @@ RUN pip3 install -U pip; pip3 install -r /flask/requirements-fastwsgi.txt
 
 EXPOSE 8080
 
-CMD python ./app-fastwsgi.py
+ENV USE_UJSON=1
+ENV USE_RAW=1
+
+CMD python ./app.py -s fastwsgi

+ 3 - 1
frameworks/Python/flask/flask-pypy-raw.dockerfile

@@ -9,4 +9,6 @@ RUN pip3 install -U pip; pip3 install -r /flask/requirements-gunicorn.txt
 
 EXPOSE 8080
 
-CMD gunicorn app_raw:app -c gunicorn_conf.py
+ENV USE_RAW=1
+
+CMD gunicorn app:app -c gunicorn_conf.py

+ 3 - 1
frameworks/Python/flask/flask-raw.dockerfile

@@ -10,4 +10,6 @@ RUN pip3 install -U pip; pip3 install -r /flask/requirements-gunicorn.txt
 
 EXPOSE 8080
 
-CMD gunicorn app_raw:app -c gunicorn_conf.py -k egg:meinheld#gunicorn_worker
+ENV USE_RAW=1
+
+CMD gunicorn app:app -c gunicorn_conf.py

+ 3 - 1
frameworks/Python/flask/flask-socketify-wsgi-pypy.dockerfile

@@ -9,4 +9,6 @@ RUN pip3 install -U pip; pip3 install -r /flask/requirements-socketify.txt
 
 EXPOSE 8080
 
-CMD python ./app-socketify-wsgi.py
+ENV USE_RAW=1
+
+CMD python ./app.py -s socketify

+ 3 - 1
frameworks/Python/flask/flask-socketify-wsgi.dockerfile

@@ -10,4 +10,6 @@ RUN pip3 install -U pip; pip3 install -r /flask/requirements-socketify.txt
 
 EXPOSE 8080
 
-CMD python ./app-socketify-wsgi.py
+ENV USE_RAW=1
+
+CMD python ./app.py -s socketify

+ 11 - 1
frameworks/Python/flask/gunicorn_conf.py

@@ -5,7 +5,7 @@ import sys
 _is_pypy = hasattr(sys, "pypy_version_info")
 _is_travis = os.environ.get("TRAVIS") == "true"
 
-workers = int(multiprocessing.cpu_count() * 2.5)
+workers = int(multiprocessing.cpu_count() * 1.5)
 if _is_travis:
     workers = 2
 
@@ -16,3 +16,13 @@ pidfile = "gunicorn.pid"
 
 if _is_pypy:
     worker_class = "sync"
+else:
+    worker_class = "meinheld.gmeinheld.MeinheldWorker"
+
+    def post_fork(server, worker):
+        import meinheld
+        import meinheld.server
+        import meinheld.patch
+        meinheld.server.set_access_logger(None)
+        meinheld.set_keepalive(keepalive)
+        meinheld.patch.patch_all()

+ 0 - 103
frameworks/Python/flask/helpers.py

@@ -1,103 +0,0 @@
-import os
-import sys
-from collections import namedtuple
-from random import randint
-
-import jinja2
-from flask import request
-
-
-if sys.version_info[0] == 3:
-    xrange = range
-
-_is_pypy = hasattr(sys, "pypy_version_info")
-if _is_pypy:
-    from psycopg2cffi.pool import ThreadedConnectionPool
-    from psycopg2cffi.extras import execute_batch
-    import ujson as orjson
-else:
-    from psycopg2.pool import ThreadedConnectionPool
-    from psycopg2.extras import execute_batch
-    import orjson
-    try:
-        import meinheld
-        import meinheld.patch
-
-        meinheld.server.set_access_logger(None)
-        meinheld.set_keepalive(30)
-        meinheld.patch.patch_all()
-    except ImportError:
-        pass
-
-
-def get_num_queries():
-    try:
-        num_queries = request.args.get("queries", 1, type=int)
-    except ValueError:
-        num_queries = 1
-    if num_queries < 1:
-        return 1
-    if num_queries > 500:
-        return 500
-    return num_queries
-
-
-def generate_ids(num_queries):
-    ids = {randint(1, 10000) for _ in xrange(num_queries)}
-    while len(ids) < num_queries:
-        ids.add(randint(1, 10000))
-    return list(sorted(ids))
-
-
-def load_fortunes_template():
-    path = os.path.join("templates", "fortune_raw.html")
-    with open(path, "r") as template_file:
-        template_text = template_file.read()
-        return jinja2.Template(template_text)
-
-
-def setup(threads):
-    pool = ThreadedConnectionPool(
-        minconn=int(threads / 4),
-        maxconn=int(threads / 4),
-        database="hello_world",
-        user="benchmarkdbuser",
-        password="benchmarkdbpass",
-        host="tfb-database",
-        port=5432,
-    )
-    template = load_fortunes_template()
-    read_row_sql = (
-        'SELECT world."randomnumber", world."id" FROM "world" WHERE id = %(id)s'
-    )
-    prepared_read_row_sql = (
-        'SELECT world."randomnumber", world."id" FROM "world" WHERE id = $1'
-    )
-    write_row_sql = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2'
-    additional_row = (0, "Additional fortune added at request time.")
-    db = pool.getconn()
-    cursor = db.cursor()
-    cursor.execute("PREPARE read_stmt (int) AS " + prepared_read_row_sql)
-    cursor.execute("PREPARE write_stmt (int, int) AS " + write_row_sql)
-    cursor.execute('PREPARE fortune AS SELECT * FROM "Fortune"')
-    del cursor
-    pool.putconn(db)
-    del db
-    return (
-        pool,
-        template,
-        read_row_sql,
-        prepared_read_row_sql,
-        write_row_sql,
-        additional_row,
-    )
-
-
-def query(arg):
-    (cursor, ident) = arg
-    cursor.execute("EXECUTE read_stmt(%s)", [ident])
-    result = cursor.fetchone()
-    return result
-
-
-Fortune = namedtuple("Fortune", ["id", "message"])

+ 2 - 2
frameworks/Python/flask/nginx.conf

@@ -6,7 +6,7 @@ error_log stderr error;
 events {
     # This needed to be increased because the nginx error log said so.
     # http://nginx.org/en/docs/ngx_core_module.html#worker_connections
-    worker_connections  65535;
+    worker_connections  4095;
     multi_accept on;
 }
 
@@ -37,7 +37,7 @@ http {
         # http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
         # http://www.techrepublic.com/article/take-advantage-of-tcp-ip-options-to-optimize-data-transmission/
         # The backlog argument to listen() is set to match net.ipv4.tcp_max_syn_backlog and net.core.somaxconn
-        listen       8080 default_server deferred reuseport backlog=65535;
+        listen       8080 default_server deferred reuseport backlog=4095;
         server_name  localhost;
 
         location / {

+ 1 - 1
frameworks/Python/flask/uwsgi.ini

@@ -2,7 +2,7 @@
 master
 ; Increase listen queue used for nginx connecting to uWSGI. This matches
 ; net.ipv4.tcp_max_syn_backlog and net.core.somaxconn.
-listen = 16384
+listen = 4096
 ; for performance
 disable-logging
 ; use UNIX sockets instead of TCP loopback for performance