Bläddra i källkod

Improved Flask code (#6688)

* Improved Flask code

* split app_raw from app
* cleaned app/ORM/SQLAlchemy code
* Rewrote app_raw/raw code from the ground up
* Bumped PyPy from PyPy2 to PyPy3 due to deprecation of Python 2.x
* Migrated from MySQL to PostgreSQL

* Updated README.md

* Migrated to PonyORM to work around issues with SQLAlchemy Cache

* Fix PyPy
Tim Armstrong 4 år sedan
förälder
incheckning
3b2474212d

+ 10 - 10
frameworks/Python/flask/README.md

@@ -17,12 +17,12 @@ Flask + Flask-SQLAlchemy
 
 
 ### Database
 ### Database
 
 
-MySQL (mysqlclient on CPython, PyMySQL on PyPy)
+PostgresQL (psycopg2 on CPython, psycopg2cffi on PyPy)
 
 
 ### Server
 ### Server
 
 
 * gunicorn+meinheld on CPython
 * gunicorn+meinheld on CPython
-* Tornado on PyPy
+* guinicorn on PyPy
 
 
 ## Test URLs
 ## Test URLs
 ### JSON Encoding 
 ### JSON Encoding 
@@ -31,16 +31,16 @@ http://localhost:8080/json
 
 
 ### Single Row Random Query
 ### Single Row Random Query
 
 
-With ORM:
-    http://localhost:8080/dbs
+With ORM (app.py):
+    http://localhost:8080/db
 
 
-Without ORM (raw):
-    http://localhost:8080/dbsraw
+Without ORM (app_raw.py):
+    http://localhost:8080/db
 
 
 ### Variable Row Query Test 
 ### Variable Row Query Test 
 
 
-With ORM:
-    http://localhost:8080/db?queries=2
+With ORM (app.py):
+    http://localhost:8080/query?queries=2
 
 
-Without ORM (raw):
-    http://localhost:8080/dbraw?queries=2
+Without ORM (app_raw.py):
+    http://localhost:8080/query?queries=2

+ 59 - 135
frameworks/Python/flask/app.py

@@ -1,192 +1,116 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
-from functools import partial
-import json
+from collections import namedtuple
 from operator import attrgetter
 from operator import attrgetter
-import os
 from random import randint
 from random import randint
 import sys
 import sys
-from email.utils import formatdate
 
 
-import flask
 from flask import Flask, request, render_template, make_response, jsonify
 from flask import Flask, request, render_template, make_response, jsonify
-from flask_sqlalchemy import SQLAlchemy
-from sqlalchemy import create_engine
-from sqlalchemy.ext import baked
+from pony import orm
 
 
 if sys.version_info[0] == 3:
 if sys.version_info[0] == 3:
     xrange = range
     xrange = range
-
 _is_pypy = hasattr(sys, 'pypy_version_info')
 _is_pypy = hasattr(sys, 'pypy_version_info')
+if _is_pypy:
+    from psycopg2cffi import compat
+    compat.register()
 
 
-DBDRIVER = 'mysql+pymysql' if _is_pypy else 'mysql'  # mysqlclient is slow on PyPy
+DBDRIVER = 'postgres'
 DBHOST = 'tfb-database'
 DBHOST = 'tfb-database'
 
 
-
 # setup
 # setup
 
 
 app = Flask(__name__)
 app = Flask(__name__)
-app.config['SQLALCHEMY_DATABASE_URI'] = DBDRIVER + '://benchmarkdbuser:benchmarkdbpass@%s:3306/hello_world?charset=utf8' % DBHOST
-app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
+app.config['STORM_DATABASE_URI'] = "{DBDRIVER}://benchmarkdbuser:benchmarkdbpass@{DBHOST}:5432/hello_world".format(DBDRIVER=DBDRIVER, DBHOST=DBHOST)
 app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
 app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
-db = SQLAlchemy(app)
-dbraw_engine = create_engine(app.config['SQLALCHEMY_DATABASE_URI'], connect_args={'autocommit': True}, pool_reset_on_return=None)
-
-bakery = baked.bakery()
-
+db = orm.Database()
+db.bind(DBDRIVER, host=DBHOST, port=5432, user="benchmarkdbuser", password="benchmarkdbpass", database="hello_world")
 
 
-# models
 
 
-class World(db.Model):
-    __tablename__ = "world"
-    id = db.Column(db.Integer, primary_key=True)
-    randomNumber = db.Column(db.Integer)
+class World(db.Entity):
+    _table_ = "world"
+    id = orm.PrimaryKey(int)
+    randomNumber = orm.Required(int, column="randomnumber")
 
 
-    # http://stackoverflow.com/questions/7102754/jsonify-a-sqlalchemy-result-set-in-flask
-    @property
-    def serialize(self):
+    def to_dict(self):
         """Return object data in easily serializeable format"""
         """Return object data in easily serializeable format"""
         return {
         return {
             'id'         : self.id,
             'id'         : self.id,
             'randomNumber': self.randomNumber
             'randomNumber': self.randomNumber
         }
         }
 
 
-    @staticmethod
-    def get(ident):
-        baked_query = bakery(lambda s: s.query(World))
-        return baked_query(db.session()).get(ident)
 
 
+class Fortune(db.Entity):
+    _table_ = "fortune"
+    id = orm.PrimaryKey(int, auto=True)
+    message = orm.Required(str)
 
 
-class Fortune(db.Model):
-    __tablename__ = "fortune"
-    id = db.Column(db.Integer, primary_key=True)
-    message = db.Column(db.String)
 
 
+db.generate_mapping(create_tables=False)
 
 
-# views
 
 
-# flask.jsonify doesn't allow array at top level for security concern.
-# So we should have oriiginal one.
-def json_response(obj):
-    res = make_response(json.dumps(obj))
-    res.mimetype = "application/json"
-    return add_date_header(res)
+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 add_date_header(res):
-    res.headers['Date'] = formatdate(timeval=None, localtime=False, usegmt=True)
-    return res
+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))
 
 
 
 
 @app.route("/json")
 @app.route("/json")
 def hello():
 def hello():
-    return add_date_header(jsonify(message='Hello, World!'))
+    return jsonify(message='Hello, World!')
 
 
 
 
[email protected]("/db")
[email protected]("/query")
 def get_random_world():
 def get_random_world():
-    num_queries = request.args.get("queries", 1, type=int)
-    if num_queries < 1:
-        num_queries = 1
-    if num_queries > 500:
-        num_queries = 500
-    worlds = [World.get(randint(1, 10000)).serialize
-              for _ in xrange(num_queries)]
-    return json_response(worlds)
+    with orm.db_session(serializable=False):
+        worlds = [World[ident].to_dict()
+              for ident in generate_ids(get_num_queries())]
+    return jsonify(worlds)
 
 
 
 
[email protected]("/dbs")
[email protected]("/db")
 def get_random_world_single():
 def get_random_world_single():
     wid = randint(1, 10000)
     wid = randint(1, 10000)
-    worlds = World.get(wid).serialize
-    return json_response(worlds)
-
+    with orm.db_session(serializable=False):
+        world = World[wid]
+    return jsonify(world.to_dict())
 
 
[email protected]("/dbraw")
-def get_random_world_raw():
-    connection = dbraw_engine.connect()
-    num_queries = request.args.get("queries", 1, type=int)
-    if num_queries < 1:
-        num_queries = 1
-    if num_queries > 500:
-        num_queries = 500
-    worlds = []
-    for i in xrange(num_queries):
-        wid = randint(1, 10000)
-        result = connection.execute("SELECT * FROM world WHERE id = " + str(wid)).fetchone()
-        worlds.append({'id': result[0], 'randomNumber': result[1]})
-    connection.close()
-    return json_response(worlds)
-
-
[email protected]("/dbsraw")
-def get_random_world_single_raw():
-    connection = dbraw_engine.connect()
-    wid = randint(1, 10000)
-    result = connection.execute("SELECT * FROM world WHERE id = " + str(wid)).fetchone()
-    worlds = {'id': result[0], 'randomNumber': result[1]}
-    connection.close()
-    return json_response(worlds)
 
 
 @app.route("/fortunes")
 @app.route("/fortunes")
 def get_fortunes():
 def get_fortunes():
-    fortunes = list(Fortune.query.all())
-    fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
+    with orm.db_session(serializable=False):
+        fortunes = list(orm.select(fortune for fortune in Fortune))
+    tmp_fortune = namedtuple("Fortune", ["id", "message"])
+    fortunes.append(tmp_fortune(id=0, message="Additional fortune added at request time."))
     fortunes.sort(key=attrgetter('message'))
     fortunes.sort(key=attrgetter('message'))
-    return add_date_header(make_response(render_template('fortunes.html', fortunes=fortunes)))
-
[email protected]("/fortunesraw")
-def get_fortunes_raw():
-    res = dbraw_engine.execute("SELECT * FROM fortune")
-    fortunes = res.fetchall()
-    res.close()
-    fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
-    fortunes.sort(key=attrgetter('message'))
-    return add_date_header(make_response(render_template('fortunes.html', fortunes=fortunes)))
+    return render_template('fortunes.html', fortunes=fortunes)
 
 
 
 
 @app.route("/updates")
 @app.route("/updates")
 def updates():
 def updates():
     """Test 5: Database Updates"""
     """Test 5: Database Updates"""
-    num_queries = request.args.get('queries', 1, type=int)
-    if num_queries < 1:
-        num_queries = 1
-    if num_queries > 500:
-        num_queries = 500
-
+    num_queries = get_num_queries()
+    ids = generate_ids(num_queries)
+    ids.sort()
     worlds = []
     worlds = []
-    rp = partial(randint, 1, 10000)
-    ids = [rp() for _ in xrange(num_queries)]
-    ids.sort()  # To avoid deadlock
-    for id in ids:
-        world = World.get(id)
-        world.randomNumber = rp()
-        worlds.append(world.serialize)
-    res = json_response(worlds)
-    db.session.commit()
-    return res
-
-
[email protected]("/raw-updates")
-def raw_updates():
-    """Test 5: Database Updates"""
-    connection = dbraw_engine.connect()
-    try:
-        num_queries = request.args.get('queries', 1, type=int)
-        if num_queries < 1:
-            num_queries = 1
-        if num_queries > 500:
-            num_queries = 500
-
-        worlds = []
-        rp = partial(randint, 1, 10000)
-        for i in xrange(num_queries):
-            world = connection.execute("SELECT * FROM world WHERE id=%s", (rp(),)).fetchone()
-            randomNumber = rp()
-            worlds.append({'id': world['id'], 'randomNumber': randomNumber})
-            connection.execute("UPDATE World SET randomNumber=%s WHERE id=%s", (randomNumber, world['id']))
-        return json_response(worlds)
-    finally:
-        connection.close()
+    with orm.db_session(serializable=False):
+        for ident in ids:
+            world = World[ident]
+            world.randomNumber = randint(1, 10000)
+            worlds.append({"id": world.id, "randomNumber": world.randomNumber})
+    return jsonify(worlds)
 
 
 
 
 @app.route('/plaintext')
 @app.route('/plaintext')
@@ -194,7 +118,7 @@ def plaintext():
     """Test 6: Plaintext"""
     """Test 6: Plaintext"""
     response = make_response(b'Hello, World!')
     response = make_response(b'Hello, World!')
     response.content_type = 'text/plain'
     response.content_type = 'text/plain'
-    return add_date_header(response)
+    return response
 
 
 
 
 try:
 try:

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

@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+from collections import namedtuple
+from operator import attrgetter, itemgetter
+from random import randint
+import sys
+
+from flask import Flask, request, render_template, make_response, jsonify
+
+
+if sys.version_info[0] == 3:
+    xrange = range
+_is_pypy = hasattr(sys, 'pypy_version_info')
+if _is_pypy:
+    from psycopg2cffi.pool import ThreadedConnectionPool
+else:
+    from psycopg2.pool import ThreadedConnectionPool
+# setup
+
+app = Flask(__name__)
+
+pool = ThreadedConnectionPool(minconn=2, maxconn=2, database="hello_world", user="benchmarkdbuser", password="benchmarkdbpass", host="tfb-database", port=5432)
+
+READ_ROW_SQL = 'SELECT world."randomnumber", world."id" FROM "world" WHERE id = %(id)s'
+WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=%(randomNumber)s WHERE id=%(id)s'
+
+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))
+
+
[email protected]("/json")
+def hello():
+    return jsonify(message='Hello, World!')
+
+
[email protected]("/query")
+def get_random_world():
+    db = pool.getconn()
+    cursor = db.cursor()
+    def query(ident):
+        cursor.execute(READ_ROW_SQL, {"id": ident})
+        result = cursor.fetchone()
+        return result
+    worlds = [{'id': result[0], 'randomNumber': result[1]} for result in map(query, generate_ids(get_num_queries()))]
+    pool.putconn(db)
+    return jsonify(worlds)
+
+
[email protected]("/db")
+def get_random_world_single():
+    db = pool.getconn()
+    cursor = db.cursor()
+    cursor.execute(READ_ROW_SQL, {"id": randint(1, 10000)})
+    result = cursor.fetchone()
+    world = {'id': result[0], 'randomNumber': result[1]}
+    pool.putconn(db)
+    return jsonify(world)
+
+
+Fortune = namedtuple("Fortune", ["id", "message"])
+
+
[email protected]("/fortunes")
+def get_fortunes():
+    db = pool.getconn()
+    cursor = db.cursor()
+    cursor.execute('SELECT * FROM "Fortune"')
+    fortunes = [Fortune(id=row[0], message=row[1]) for row in cursor.fetchall()]
+    fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
+    fortunes.sort(key=attrgetter('message'))
+    pool.putconn(db)
+    return render_template('fortunes.html', 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)
+    updates = generate_ids(num_queries)
+    def query(ident):
+        cursor.execute(READ_ROW_SQL, {"id": ident})
+        result = cursor.fetchone()
+        return result
+    list(map(query, generate_ids(get_num_queries())))
+    worlds = [{"id": ident, "randomNumber": update} for ident, update in zip(ids, updates)]
+    for world in worlds:
+        cursor.execute(WRITE_ROW_SQL, world)
+    db.commit()
+    pool.putconn(db)
+    return jsonify(worlds)
+
+
[email protected]('/plaintext')
+def plaintext():
+    """Test 6: Plaintext"""
+    response = 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)

+ 31 - 28
frameworks/Python/flask/benchmark_config.json

@@ -3,15 +3,15 @@
   "tests": [{
   "tests": [{
     "default": {
     "default": {
       "json_url": "/json",
       "json_url": "/json",
-      "db_url": "/dbs",
-      "query_url": "/db?queries=",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
       "fortune_url": "/fortunes",
       "fortune_url": "/fortunes",
       "update_url": "/updates?queries=",
       "update_url": "/updates?queries=",
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Micro",
       "classification": "Micro",
-      "database": "MySQL",
+      "database": "Postgres",
       "framework": "flask",
       "framework": "flask",
       "language": "Python",
       "language": "Python",
       "flavor": "Python3",
       "flavor": "Python3",
@@ -25,14 +25,16 @@
       "versus": "wsgi"
       "versus": "wsgi"
     },
     },
     "raw": {
     "raw": {
-      "db_url": "/dbsraw",
-      "query_url": "/dbraw?queries=",
-      "fortune_url": "/fortunesraw",
-      "update_url": "/raw-updates?queries=",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Micro",
       "classification": "Micro",
-      "database": "MySQL",
+      "database": "Postgres",
       "framework": "flask",
       "framework": "flask",
       "language": "Python",
       "language": "Python",
       "flavor": "CPython",
       "flavor": "CPython",
@@ -45,62 +47,63 @@
       "notes": "",
       "notes": "",
       "versus": "wsgi"
       "versus": "wsgi"
     },
     },
-    "pypy2": {
+    "pypy": {
       "json_url": "/json",
       "json_url": "/json",
-      "db_url": "/dbs",
-      "query_url": "/db?queries=",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
       "fortune_url": "/fortunes",
       "fortune_url": "/fortunes",
       "update_url": "/updates?queries=",
       "update_url": "/updates?queries=",
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Micro",
       "classification": "Micro",
-      "database": "MySQL",
+      "database": "Postgres",
       "framework": "flask",
       "framework": "flask",
       "language": "Python",
       "language": "Python",
-      "flavor": "PyPy2",
+      "flavor": "PyPy",
       "orm": "Full",
       "orm": "Full",
       "platform": "None",
       "platform": "None",
-      "webserver": "Tornado",
+      "webserver": "Meinheld",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "Flask",
       "display_name": "Flask",
-      "notes": "PyPy2",
+      "notes": "PyPy",
       "versus": "wsgi"
       "versus": "wsgi"
     },
     },
-    "pypy2-raw": {
-      "setup_file": "setup_pypy2",
-      "db_url": "/dbsraw",
-      "query_url": "/dbraw?queries=",
-      "fortune_url": "/fortunesraw",
-      "update_url": "/raw-updates?queries=",
+    "pypy-raw": {
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Micro",
       "classification": "Micro",
-      "database": "MySQL",
+      "database": "Postgres",
       "framework": "flask",
       "framework": "flask",
       "language": "Python",
       "language": "Python",
-      "flavor": "PyPy2",
+      "flavor": "PyPy",
       "orm": "Raw",
       "orm": "Raw",
-      "platform": "Tornado",
+      "platform": "Meinheld",
       "webserver": "None",
       "webserver": "None",
       "os": "Linux",
       "os": "Linux",
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "Flask-raw",
       "display_name": "Flask-raw",
-      "notes": "PyPy2",
+      "notes": "PyPy",
       "versus": "wsgi"
       "versus": "wsgi"
     },
     },
     "nginx-uwsgi": {
     "nginx-uwsgi": {
       "json_url": "/json",
       "json_url": "/json",
-      "db_url": "/dbs",
-      "query_url": "/db?queries=",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
       "fortune_url": "/fortunes",
       "fortune_url": "/fortunes",
       "update_url": "/updates?queries=",
       "update_url": "/updates?queries=",
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
       "classification": "Micro",
       "classification": "Micro",
-      "database": "MySQL",
+      "database": "Postgres",
       "framework": "flask",
       "framework": "flask",
       "language": "Python",
       "language": "Python",
       "flavor": "CPython",
       "flavor": "CPython",

+ 29 - 25
frameworks/Python/flask/config.toml

@@ -4,13 +4,13 @@ name = "flask"
 [main]
 [main]
 urls.plaintext = "/plaintext"
 urls.plaintext = "/plaintext"
 urls.json = "/json"
 urls.json = "/json"
-urls.db = "/dbs"
-urls.query = "/db?queries="
+urls.db = "/db"
+urls.query = "/queries?queries="
 urls.update = "/updates?queries="
 urls.update = "/updates?queries="
 urls.fortune = "/fortunes"
 urls.fortune = "/fortunes"
 approach = "Realistic"
 approach = "Realistic"
 classification = "Micro"
 classification = "Micro"
-database = "MySQL"
+database = "Postgres"
 database_os = "Linux"
 database_os = "Linux"
 os = "Linux"
 os = "Linux"
 orm = "Full"
 orm = "Full"
@@ -19,13 +19,15 @@ webserver = "None"
 versus = "wsgi"
 versus = "wsgi"
 
 
 [raw]
 [raw]
-urls.db = "/dbsraw"
-urls.query = "/dbraw?queries="
-urls.update = "/raw-updates?queries="
-urls.fortune = "/fortunesraw"
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+urls.db = "/db"
+urls.query = "/queries?queries="
+urls.update = "/updates?queries="
+urls.fortune = "/fortunes"
 approach = "Realistic"
 approach = "Realistic"
 classification = "Micro"
 classification = "Micro"
-database = "MySQL"
+database = "Postgres"
 database_os = "Linux"
 database_os = "Linux"
 os = "Linux"
 os = "Linux"
 orm = "Raw"
 orm = "Raw"
@@ -36,48 +38,50 @@ versus = "wsgi"
 [nginx-uwsgi]
 [nginx-uwsgi]
 urls.plaintext = "/plaintext"
 urls.plaintext = "/plaintext"
 urls.json = "/json"
 urls.json = "/json"
-urls.db = "/dbs"
-urls.query = "/db?queries="
+urls.db = "/db"
+urls.query = "/queries?queries="
 urls.update = "/updates?queries="
 urls.update = "/updates?queries="
 urls.fortune = "/fortunes"
 urls.fortune = "/fortunes"
 approach = "Realistic"
 approach = "Realistic"
 classification = "Micro"
 classification = "Micro"
-database = "MySQL"
+database = "Postgres"
 database_os = "Linux"
 database_os = "Linux"
 os = "Linux"
 os = "Linux"
 orm = "Full"
 orm = "Full"
-platform = "None"
+platform = "Meinheld"
 webserver = "nginx"
 webserver = "nginx"
 versus = "wsgi"
 versus = "wsgi"
 
 
-[pypy2-raw]
-urls.db = "/dbsraw"
-urls.query = "/dbraw?queries="
-urls.update = "/raw-updates?queries="
-urls.fortune = "/fortunesraw"
+[pypy-raw]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+urls.db = "/db"
+urls.query = "/queries?queries="
+urls.update = "/updates?queries="
+urls.fortune = "/fortunes"
 approach = "Realistic"
 approach = "Realistic"
 classification = "Micro"
 classification = "Micro"
-database = "MySQL"
+database = "Postgres"
 database_os = "Linux"
 database_os = "Linux"
 os = "Linux"
 os = "Linux"
 orm = "Raw"
 orm = "Raw"
-platform = "Tornado"
+platform = "Meinheld"
 webserver = "None"
 webserver = "None"
 versus = "wsgi"
 versus = "wsgi"
 
 
-[pypy2]
+[pypy]
 urls.plaintext = "/plaintext"
 urls.plaintext = "/plaintext"
 urls.json = "/json"
 urls.json = "/json"
-urls.db = "/dbs"
-urls.query = "/db?queries="
+urls.db = "/db"
+urls.query = "/queries?queries="
 urls.update = "/updates?queries="
 urls.update = "/updates?queries="
 urls.fortune = "/fortunes"
 urls.fortune = "/fortunes"
 approach = "Realistic"
 approach = "Realistic"
 classification = "Micro"
 classification = "Micro"
-database = "MySQL"
+database = "Postgres"
 database_os = "Linux"
 database_os = "Linux"
 os = "Linux"
 os = "Linux"
 orm = "Full"
 orm = "Full"
-platform = "None"
-webserver = "Tornado"
+platform = "Meinheld"
+webserver = "None"
 versus = "wsgi"
 versus = "wsgi"

+ 6 - 7
frameworks/Python/flask/flask-nginx-uwsgi.dockerfile

@@ -1,17 +1,16 @@
-FROM python:3.6.6-stretch
+FROM python:3.8-buster
 
 
 RUN curl -s http://nginx.org/keys/nginx_signing.key | apt-key add -
 RUN curl -s http://nginx.org/keys/nginx_signing.key | apt-key add -
-RUN echo "deb http://nginx.org/packages/debian/ stretch nginx" >> /etc/apt/sources.list
-RUN echo "deb-src http://nginx.org/packages/debian/ stretch nginx" >> /etc/apt/sources.list
+RUN echo "deb http://nginx.org/packages/debian/ buster nginx" >> /etc/apt/sources.list
+RUN echo "deb-src http://nginx.org/packages/debian/ buster nginx" >> /etc/apt/sources.list
 
 
 RUN apt-get update -yqq && apt-get install -yqq nginx
 RUN apt-get update -yqq && apt-get install -yqq nginx
-
+RUN apt-get install libpq-dev python3-dev -y
+ADD ./requirements.txt /flask/requirements.txt
+RUN pip3 install -r /flask/requirements.txt
 ADD ./ /flask
 ADD ./ /flask
-
 WORKDIR /flask
 WORKDIR /flask
 
 
-RUN pip3 install -r /flask/requirements.txt
-
 RUN sed -i 's|include .*/conf/uwsgi_params;|include /etc/nginx/uwsgi_params;|g' /flask/nginx.conf
 RUN sed -i 's|include .*/conf/uwsgi_params;|include /etc/nginx/uwsgi_params;|g' /flask/nginx.conf
 
 
 EXPOSE 8080
 EXPOSE 8080

+ 12 - 0
frameworks/Python/flask/flask-pypy-raw.dockerfile

@@ -0,0 +1,12 @@
+FROM pypy:3.7-buster
+
+RUN apt-get update
+RUN apt-get install libpq-dev python3-dev -y
+ADD ./requirements-pypy.txt /flask/requirements-pypy.txt
+RUN pip3 install -r /flask/requirements-pypy.txt
+ADD ./ /flask
+WORKDIR /flask
+
+EXPOSE 8080
+
+CMD gunicorn app_raw:app -c gunicorn_conf.py

+ 12 - 0
frameworks/Python/flask/flask-pypy.dockerfile

@@ -0,0 +1,12 @@
+FROM pypy:3
+
+RUN apt-get update
+RUN apt-get install libpq-dev python3-dev -y
+ADD ./requirements-pypy.txt /flask/requirements-pypy.txt
+RUN pip3 install -r /flask/requirements-pypy.txt
+ADD ./ /flask
+WORKDIR /flask
+
+EXPOSE 8080
+
+CMD gunicorn app:app -c gunicorn_conf.py

+ 0 - 13
frameworks/Python/flask/flask-pypy2-raw.dockerfile

@@ -1,13 +0,0 @@
-FROM pypy:2-5.10
-
-ADD ./ /flask
-
-WORKDIR /flask
-
-RUN pip install -r /flask/requirements-pypy.txt
-
-WORKDIR /flask
-
-EXPOSE 8080
-
-CMD gunicorn app:app -c gunicorn_conf.py

+ 0 - 13
frameworks/Python/flask/flask-pypy2.dockerfile

@@ -1,13 +0,0 @@
-FROM pypy:2-5.10
-
-ADD ./ /flask
-
-WORKDIR /flask
-
-RUN pip install -r /flask/requirements-pypy.txt
-
-WORKDIR /flask
-
-EXPOSE 8080
-
-CMD gunicorn app:app -c gunicorn_conf.py

+ 7 - 5
frameworks/Python/flask/flask-raw.dockerfile

@@ -1,11 +1,13 @@
-FROM python:3.6.6-stretch
+FROM python:3.8-buster
 
 
-ADD ./ /flask
-
-WORKDIR /flask
 
 
+RUN apt-get update
+RUN apt-get install libpq-dev python3-dev -y
+ADD ./requirements.txt /flask/requirements.txt
 RUN pip3 install -r /flask/requirements.txt
 RUN pip3 install -r /flask/requirements.txt
+ADD ./ /flask
+WORKDIR /flask
 
 
 EXPOSE 8080
 EXPOSE 8080
 
 
-CMD gunicorn app:app -c gunicorn_conf.py
+CMD gunicorn app_raw:app -c gunicorn_conf.py

+ 6 - 4
frameworks/Python/flask/flask.dockerfile

@@ -1,10 +1,12 @@
-FROM python:3.6.6-stretch
+FROM python:3.8-buster
 
 
-ADD ./ /flask
-
-WORKDIR /flask
 
 
+RUN apt-get update
+RUN apt-get install libpq-dev python3-dev -y
+ADD ./requirements.txt /flask/requirements.txt
 RUN pip3 install -r /flask/requirements.txt
 RUN pip3 install -r /flask/requirements.txt
+ADD ./ /flask
+WORKDIR /flask
 
 
 EXPOSE 8080
 EXPOSE 8080
 
 

+ 2 - 2
frameworks/Python/flask/gunicorn_conf.py

@@ -5,7 +5,7 @@ import sys
 _is_pypy = hasattr(sys, 'pypy_version_info')
 _is_pypy = hasattr(sys, 'pypy_version_info')
 _is_travis = os.environ.get('TRAVIS') == 'true'
 _is_travis = os.environ.get('TRAVIS') == 'true'
 
 
-workers = multiprocessing.cpu_count() * 8
+workers = multiprocessing.cpu_count()*3
 if _is_travis:
 if _is_travis:
     workers = 2
     workers = 2
 
 
@@ -15,7 +15,7 @@ errorlog = '-'
 pidfile = 'gunicorn.pid'
 pidfile = 'gunicorn.pid'
 
 
 if _is_pypy:
 if _is_pypy:
-    worker_class = "tornado"
+    worker_class = "sync"
 else:
 else:
     worker_class = "meinheld.gmeinheld.MeinheldWorker"
     worker_class = "meinheld.gmeinheld.MeinheldWorker"
 
 

+ 13 - 18
frameworks/Python/flask/requirements-pypy.txt

@@ -1,18 +1,13 @@
-backports-abc==0.5
-certifi==2018.1.18
-cffi==1.11.2
-click==6.7
-Flask==0.12.2
-Flask-SQLAlchemy==2.3.2
-greenlet==0.4.14
-gunicorn==19.9.0
-itsdangerous==0.24
-Jinja2==2.11.3
-MarkupSafe==1.0
-PyMySQL==0.8.0
-readline==6.2.4.1
-singledispatch==3.4.0.3
-six==1.11.0
-SQLAlchemy==1.2.2
-tornado==4.5.3
-Werkzeug==0.14.1
+click==8.0.1
+pony==0.7.14
+psycopg2cffi>=2.7
+Flask==1.1.2
+tornado==6.1
+gevent>=1.4
+gunicorn==20.1.0
+itsdangerous==2.0.1
+Jinja2==3.0.1
+MarkupSafe==2.0.1
+meinheld==1.0.2
+uWSGI==2.0.19.1
+Werkzeug==2.0.1

+ 14 - 13
frameworks/Python/flask/requirements.txt

@@ -1,13 +1,14 @@
-click==6.7
-Flask==0.12.2
-Flask-SQLAlchemy==2.3.2
-greenlet==0.4.14
-gunicorn==19.9.0
-itsdangerous==0.24
-Jinja2==2.11.3
-MarkupSafe==1.0
-meinheld==0.6.1
-mysqlclient==1.3.12
-SQLAlchemy==1.2.2
-uWSGI==2.0.17.1
-Werkzeug==0.14.1
+click==8.0.1
+pony==0.7.14
+psycopg2==2.9.1
+psycopg2-binary==2.9.1
+psycopg2-pool==1.1
+Flask==1.1.2
+greenlet==0.4.15
+gunicorn==20.1.0
+itsdangerous==2.0.1
+Jinja2==3.0.1
+MarkupSafe==2.0.1
+meinheld==1.0.2
+uWSGI==2.0.19.1
+Werkzeug==2.0.1