Browse Source

Crax framework added to benchmarks (#5955)

* Crax framework added to benchmarks

* Crax framework added to benchmarks

* Crax framework added to benchmarks
Eugene Mercousheu 5 years ago
parent
commit
0910ed25e4

+ 1 - 1
.travis.yml

@@ -69,7 +69,7 @@ env:
     - 'TESTDIR="PHP/cakephp PHP/codeigniter PHP/fat-free PHP/fuel PHP/phpixie PHP/slim PHP/symfony PHP/yii2 PHP/zend PHP/spiral"'
     - 'TESTDIR="PHP/amp PHP/hhvm PHP/peachpie PHP/php-ngx PHP/phalcon"'
     - 'TESTDIR="PHP/hamlet PHP/laravel PHP/lumen PHP/swoole PHP/ubiquity PHP/hyperf PHP/sw-fw-less PHP/imi PHP/simps PHP/one"'
-    - 'TESTDIR="Python/aiohttp Python/api_hour Python/apidaora Python/blacksheep Python/bottle Python/cherrypy Python/django Python/emmett Python/eve Python/falcon Python/fastapi Python/flask"'
+    - 'TESTDIR="Python/aiohttp Python/api_hour Python/apidaora Python/blacksheep Python/bottle Python/cherrypy Python/crax Python/django Python/emmett Python/eve Python/falcon Python/fastapi Python/flask"'
     - 'TESTDIR="Python/hug Python/japronto Python/klein Python/morepath Python/pyramid Python/quart Python/responder Python/sanic Python/spyne Python/starlette"'
     - 'TESTDIR="Python/tornado Python/turbogears Python/uvicorn Python/uwsgi Python/vibora Python/web2py Python/webware Python/weppy Python/wsgi"'
     - 'TESTDIR="Ruby/agoo Ruby/grape Ruby/h2o_mruby Ruby/padrino Ruby/rack Ruby/rack-sequel"'

+ 22 - 0
frameworks/Python/crax/README.md

@@ -0,0 +1,22 @@
+# [Crax](https://crax.wiki/) Benchmark Test
+
+This is the Crax portion of a [benchmarking tests suite](../../)
+comparing a variety of web development platforms.
+
+The information below is specific to Crax. For further guidance,
+review the [documentation](https://github.com/TechEmpower/FrameworkBenchmarks/wiki).
+Also note that there is additional information provided in
+the [Python README](../).
+
+## Description
+
+[Crax](https://github.com/crax-framework/crax) is a framework or a pack of tools for web development.
+
+## Test Paths & Sources
+
+All of the test implementations are located within a single file ([app.py](hello/app.py)).
+
+## Resources
+
+* [Crax on GitHub](https://github.com/crax-framework/crax)
+* [Crax Wiki](https://crax.wiki/)

+ 27 - 0
frameworks/Python/crax/benchmark_config.json

@@ -0,0 +1,27 @@
+{
+  "framework": "crax",
+  "tests": [{
+    "default": {
+      "json_url": "/json",
+      "fortune_url": "/fortunes",
+      "plaintext_url": "/plaintext",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "update_url": "/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "micro",
+      "framework": "crax",
+      "language": "Python",
+      "flavor": "Python3",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "orm": "Raw",
+      "database_os": "Linux",
+      "database": "Postgres",
+      "display_name": "Crax",
+      "notes": ""
+    }
+  }]
+}

+ 10 - 0
frameworks/Python/crax/crax.dockerfile

@@ -0,0 +1,10 @@
+FROM python:3.8
+
+ADD ./ /crax
+
+WORKDIR /crax
+
+RUN pip3 install cython==0.29.13 && \
+    pip3 install -r /crax/requirements.txt
+
+CMD gunicorn hello.app:app -k uvicorn.workers.UvicornWorker -c crax_conf.py

+ 14 - 0
frameworks/Python/crax/crax_conf.py

@@ -0,0 +1,14 @@
+import multiprocessing
+import os
+
+_is_travis = os.environ.get('TRAVIS') == 'true'
+
+workers = multiprocessing.cpu_count()
+if _is_travis:
+    workers = 2
+
+bind = "0.0.0.0:8080"
+keepalive = 120
+errorlog = '-'
+pidfile = '/tmp/crax.pid'
+loglevel = 'error'

+ 0 - 0
frameworks/Python/crax/hello/__init__.py


+ 90 - 0
frameworks/Python/crax/hello/app.py

@@ -0,0 +1,90 @@
+import os
+from operator import itemgetter
+from random import randint
+import asyncpg
+from crax import Crax
+from crax.response_types import BaseResponse, JSONResponse
+from crax.urls import Route, Url
+from crax.views import JSONView, TemplateView
+
+READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1'
+WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2'
+
+
+async def setup_database():
+    global connection_pool
+    connection_pool = await asyncpg.create_pool(
+        user=os.getenv('PGUSER', 'benchmarkdbuser'),
+        password=os.getenv('PGPASS', 'benchmarkdbpass'),
+        database='hello_world',
+        host='tfb-database',
+        port=5432
+    )
+
+
+def get_num_queries(request):
+    try:
+        query_count = int(request.query["queries"][0])
+    except (KeyError, IndexError, ValueError):
+        return 1
+    if query_count < 1:
+        return 1
+    if query_count > 500:
+        return 500
+    return query_count
+
+
+class TestSingleQuery(JSONView):
+    async def get(self):
+        row_id = randint(1, 10000)
+        async with connection_pool.acquire() as connection:
+            if self.request.path == '/db':
+                res = await connection.fetchval(READ_ROW_SQL, row_id)
+                self.context = {'id': row_id, 'randomNumber': res}
+
+
+class TestMultiQueries(JSONView):
+    async def get(self):
+        row_ids = [randint(1, 10000) for _ in range(get_num_queries(self.request))]
+        worlds = []
+        async with connection_pool.acquire() as connection:
+            statement = await connection.prepare(READ_ROW_SQL)
+            for row_id in row_ids:
+                number = await statement.fetchval(row_id)
+                worlds.append({'id': row_id, 'randomNumber': number})
+            self.context = worlds
+
+
+class TestUpdates(JSONView):
+    async def get(self):
+        updates = [(randint(1, 10000), randint(1, 10000)) for _ in range(get_num_queries(self.request))]
+        worlds = [{'id': row_id, 'randomNumber': number} for row_id, number in updates]
+        async with connection_pool.acquire() as connection:
+            statement = await connection.prepare(READ_ROW_SQL)
+            for row_id, number in updates:
+                await statement.fetchval(row_id)
+            await connection.executemany(WRITE_ROW_SQL, updates)
+            self.context = worlds
+
+
+class TestSingleFortunes(TemplateView):
+    template = "fortune.html"
+
+    async def get(self):
+        async with connection_pool.acquire() as connection:
+            fortunes = await connection.fetch('SELECT * FROM Fortune')
+            fortunes.append([0, 'Additional fortune added at request time.'])
+            fortunes.sort(key=itemgetter(1))
+            self.context["fortunes"] = fortunes
+
+
+APPLICATIONS = ["hello"]
+URL_PATTERNS = [
+    Route(Url('/json'), JSONResponse(None, {'message': 'Hello, world!'})),
+    Route(Url('/plaintext'), BaseResponse(None, b'Hello, world!')),
+    Route(Url('/db'), TestSingleQuery),
+    Route(Url('/queries'), TestMultiQueries),
+    Route(Url('/updates'), TestUpdates),
+    Route(Url('/fortunes'), TestSingleFortunes)
+]
+app = Crax('hello.app', debug=True, on_startup=setup_database)

+ 10 - 0
frameworks/Python/crax/hello/templates/fortune.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body>
+<table>
+<tr><th>id</th><th>message</th></tr>
+{% for fortune in fortunes %}<tr><td>{{ fortune[0] }}</td><td>{{ fortune[1]|e }}</td></tr>
+{% endfor %}</table>
+</body>
+</html>

+ 4 - 0
frameworks/Python/crax/requirements.txt

@@ -0,0 +1,4 @@
+crax[postgresql]==0.1.5
+gunicorn==20.0.4
+uvloop==0.14.0
+uvicorn==0.11.8