Просмотр исходного кода

Merge branch 'master' of https://github.com/TechEmpower/FrameworkBenchmarks into update-php-laravel-to-4.2

Brittany Mazza 10 лет назад
Родитель
Сommit
41319000a5
37 измененных файлов с 770 добавлено и 18 удалено
  1. 1 0
      .travis.yml
  2. 2 2
      config/create-redis.sh
  3. 4 0
      frameworks/Python/.gitignore
  4. 4 0
      frameworks/Python/API-Hour/bash_profile.sh
  5. 27 0
      frameworks/Python/API-Hour/benchmark_config
  6. 64 0
      frameworks/Python/API-Hour/hello/.gitignore
  7. 13 0
      frameworks/Python/API-Hour/hello/LICENSE
  8. 22 0
      frameworks/Python/API-Hour/hello/README.rst
  9. 3 0
      frameworks/Python/API-Hour/hello/etc/default/hello
  10. 15 0
      frameworks/Python/API-Hour/hello/etc/hello/api_hour/gunicorn_conf.py
  11. 71 0
      frameworks/Python/API-Hour/hello/etc/hello/api_hour/logging.ini
  12. 9 0
      frameworks/Python/API-Hour/hello/etc/hello/main/main.yaml
  13. 79 0
      frameworks/Python/API-Hour/hello/etc/init.d/hello
  14. 7 0
      frameworks/Python/API-Hour/hello/etc/logrotate.d/hello
  15. 5 0
      frameworks/Python/API-Hour/hello/etc/monit/conf.d/hello
  16. 126 0
      frameworks/Python/API-Hour/hello/etc/rsyslog.conf
  17. 69 0
      frameworks/Python/API-Hour/hello/hello/__init__.py
  18. 1 0
      frameworks/Python/API-Hour/hello/hello/endpoints/__init__.py
  19. 53 0
      frameworks/Python/API-Hour/hello/hello/endpoints/world.py
  20. 14 0
      frameworks/Python/API-Hour/hello/hello/services/__init__.py
  21. 60 0
      frameworks/Python/API-Hour/hello/hello/services/world.py
  22. 20 0
      frameworks/Python/API-Hour/hello/hello/templates/fortunes.html.j2
  23. 3 0
      frameworks/Python/API-Hour/hello/hello/utils/__init__.py
  24. 8 0
      frameworks/Python/API-Hour/install.sh
  25. 11 0
      frameworks/Python/API-Hour/requirements.txt
  26. 3 0
      frameworks/Python/API-Hour/setup.sh
  27. 9 0
      frameworks/Python/API-Hour/source_code
  28. 0 0
      frameworks/Python/django/hello/hello/__init__.py
  29. 0 0
      frameworks/Python/django/hello/world/__init__.py
  30. 23 0
      frameworks/Python/pyramid/frameworkbenchmarks/__init__.py
  31. 4 4
      frameworks/Python/pyramid/frameworkbenchmarks/views.py
  32. 6 5
      frameworks/Scala/play-scala-mongodb/README.md
  33. 29 4
      frameworks/Scala/play-scala-mongodb/app/controllers/Application.scala
  34. 1 1
      frameworks/Scala/play-scala-mongodb/benchmark_config
  35. 2 1
      frameworks/Scala/play-scala-mongodb/conf/routes
  36. 1 1
      frameworks/Scala/play-scala-mongodb/project/Build.scala
  37. 1 0
      toolset/setup/linux/languages/python3.sh

+ 1 - 0
.travis.yml

@@ -115,6 +115,7 @@ env:
     - "TESTDIR=PHP/php-zend-framework"
     - "TESTDIR=PHP/php-zend-framework1"
     - "TESTDIR=PHP/phreeze"
+    - "TESTDIR=Python/API-Hour"
     - "TESTDIR=Python/bottle"
     - "TESTDIR=Python/django"
     - "TESTDIR=Python/falcon"

+ 2 - 2
config/create-redis.sh

@@ -21,5 +21,5 @@ echo "RPUSH fortunes \"Any program that runs right is obsolete.\"" | redis-cli
 echo "RPUSH fortunes \"A list is only as strong as its weakest link. — Donald Knuth\"" | redis-cli
 echo "RPUSH fortunes \"Feature: A bug with seniority.\"" | redis-cli
 echo "RPUSH fortunes \"Computers make very fast, very accurate mistakes.\"" | redis-cli
-echo "RPUSH fortunes \"<script>alert(\\"This should not be displayed in a browser alert box.\\");</script>\"" | redis-cli
-echo "RPUSH fortunes \"フレームワークのベンチマーク\"" | redis-cli
+echo "RPUSH fortunes \"<script>alert(\\\"This should not be displayed in a browser alert box.\\\");</script>\"" | redis-cli
+echo "RPUSH fortunes \"フレームワークのベンチマーク\"" | redis-cli

+ 4 - 0
frameworks/Python/.gitignore

@@ -1,2 +1,6 @@
 uwsgi_params
 *.pid
+
+# ignore virtual environments
+pyvenv/
+venv/

+ 4 - 0
frameworks/Python/API-Hour/bash_profile.sh

@@ -0,0 +1,4 @@
+export PY3_ROOT=$IROOT/py3
+export PY3=$PY3_ROOT/bin/python
+export PY3_PIP=$PY3_ROOT/bin/pip3
+export PY3_API_HOUR=$PY3_ROOT/bin/api_hour

+ 27 - 0
frameworks/Python/API-Hour/benchmark_config

@@ -0,0 +1,27 @@
+{
+  "framework": "aiohttp.web",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8008,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "aiohttp.web",
+      "language": "Python",
+      "orm": "Raw",
+      "platform": "API-Hour",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "API-Hour",
+      "notes": "Python 3.4 + AsyncIO"
+    }
+  }]
+}

+ 64 - 0
frameworks/Python/API-Hour/hello/.gitignore

@@ -0,0 +1,64 @@
+# Created by .gitignore support plugin (hsz.mobi)
+### Python template
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.cache
+nosetests.xml
+coverage.xml
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# PyCharm/IDEA files
+.idea
+*.iml
+
+# Virtualenvs
+venv/
+pyvenv/

+ 13 - 0
frameworks/Python/API-Hour/hello/LICENSE

@@ -0,0 +1,13 @@
+Copyright [2015] [Eyepea Dev Team]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 22 - 0
frameworks/Python/API-Hour/hello/README.rst

@@ -0,0 +1,22 @@
+hello
+=====
+
+Install
+-------
+
+#. Follow pythonz install doc
+#. pythonz install 3.4.2
+#. cd /opt
+#. Git clone your app here
+#. cd /opt/hello/
+#. /usr/local/pythonz/pythons/CPython-3.4.2/bin/pyvenv pyvenv
+#. . pyvenv/bin/activate
+#. pip install -r requirements.txt
+#. cd /etc/init.d/ && ln -s /opt/hello/etc/init.d/hello
+#. cd /etc/default/ && ln -s /opt/hello/etc/default/hello
+#. cd /etc/monit/conf.d/ && ln -s /opt/hello/etc/monit/conf.d/hello
+#. update-rc.d hello defaults
+#. cp -a /opt/hello/etc/hello /etc/
+#. Adapt rsyslog and lograte
+#. service hello start
+#. service monit restart

+ 3 - 0
frameworks/Python/API-Hour/hello/etc/default/hello

@@ -0,0 +1,3 @@
+# Start the daemon by default, let the user disable it.
+START_DAEMON=yes
+DAEMON_ARGS="--config_dir=/etc/hello"

+ 15 - 0
frameworks/Python/API-Hour/hello/etc/hello/api_hour/gunicorn_conf.py

@@ -0,0 +1,15 @@
+import multiprocessing
+import os
+
+_is_travis = os.environ.get('TRAVIS') == 'true'
+
+workers = multiprocessing.cpu_count() * 2
+if _is_travis:
+    workers = 2
+
+bind = "0.0.0.0:8008"
+keepalive = 120
+errorlog = '-'
+pidfile = 'api_hour.pid'
+pythonpath = 'hello'
+backlog = 10240000

+ 71 - 0
frameworks/Python/API-Hour/hello/etc/hello/api_hour/logging.ini

@@ -0,0 +1,71 @@
+[formatters]
+keys=detailed,simple
+
+[handlers]
+keys=console,syslog,smtp
+
+[loggers]
+keys=root,warnings,asyncio,gunicorn,aiohttp,api_hour,hello
+
+[formatter_simple]
+format=%(name)s:%(levelname)s %(asctime)s %(module)s.py => %(message)s
+
+[formatter_detailed]
+format=%(name)s:%(levelname)s %(asctime)s %(module)s.py:%(lineno)d => %(message)s
+
+[handler_console]
+class=StreamHandler
+args=(sys.stdout,)
+formatter=detailed
+
+[handler_syslog]
+class=handlers.SysLogHandler
+args=('/dev/log', handlers.SysLogHandler.LOG_LOCAL6)
+formatter=detailed
+
+[handler_smtp]
+class=handlers.SMTPHandler
+level=WARN
+args=('127.0.0.1', '[email protected]', ['[email protected]'], 'hello error on server: hello')
+formatter=detailed
+
+[logger_root]
+level=ERROR
+handlers=console,syslog
+
+# You can add smtp in handlers list to receive e-mail alerts
+[logger_warnings]
+level=WARN
+handlers=console,syslog
+qualname=py.warnings
+propagate=0
+
+[logger_asyncio]
+level=WARN
+handlers=console,syslog
+qualname=asyncio
+propagate=0
+
+[logger_gunicorn]
+level=WARN
+handlers=console,syslog
+qualname=gunicorn
+propagate=0
+
+[logger_aiohttp]
+level=WARN
+handlers=console,syslog
+qualname=aiohttp
+propagate=0
+
+[logger_api_hour]
+level=WARN
+handlers=console,syslog
+qualname=api_hour
+propagate=0
+
+[logger_hello]
+level=WARN
+handlers=console,syslog
+qualname=hello
+propagate=0

+ 9 - 0
frameworks/Python/API-Hour/hello/etc/hello/main/main.yaml

@@ -0,0 +1,9 @@
+engines:
+  pg:
+    host: 127.0.0.1
+    port: 5432
+    dbname: hello_world
+    user: benchmarkdbuser
+    password: benchmarkdbpass
+    minsize: 40
+    maxsize: 40

+ 79 - 0
frameworks/Python/API-Hour/hello/etc/init.d/hello

@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+### BEGIN INIT INFO
+# Provides:          hello
+# Required-Start:    $local_fs $remote_fs $network $syslog
+# Required-Stop:     $local_fs $remote_fs $network $syslog
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: Startup daemon script for hello
+### END INIT INFO
+#
+# Author: Ludovic Gasc <[email protected]>
+set -e
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
+DAEMONNAME=hello
+RUNDIR=/run/$DAEMONNAME
+DAEMON=/opt/hello/${DAEMONNAME}_cli
+PIDFILE=/run/lock/${DAEMONNAME}_0
+DAEMON_ARGS=""
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Create RUNDIR if it doesn't exist
+[ -d "$RUNDIR" ] || mkdir -p "$RUNDIR"
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Load the VERBOSE setting and other rcS variables
+[ -f /etc/default/rcS ] && . /etc/default/rcS
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+case "$1" in
+  start)
+    log_daemon_msg "Starting" "$DAEMONNAME"
+    if start-stop-daemon -b --start --pidfile $PIDFILE --startas $DAEMON -- $DAEMON_ARGS;
+    then
+        log_end_msg 0
+    else
+        log_end_msg 1
+    fi
+    ;;
+  stop)
+    log_daemon_msg "Stopping" "$DAEMONNAME"
+    if start-stop-daemon --stop --retry 5 --pidfile $PIDFILE;
+     then
+         log_end_msg 0
+     else
+         log_end_msg 1
+     fi
+    ;;
+  reload|force-reload)
+    log_daemon_msg "Reloading" "$DAEMONNAME"
+    if start-stop-daemon --stop --signal 1 --pidfile $PIDFILE --startas $DAEMON;
+     then
+         log_end_msg 0
+     else
+         log_end_msg 1
+     fi
+    ;;
+  restart)
+    $0 stop
+    $0 start
+    ;;
+  status)
+    status_of_proc -p $PIDFILE "$DAEMON" $DAEMONNAME && exit 0 || exit $?
+    ;;
+  *)
+    echo "Usage: $0 {start|stop|reload|force-reload|restart|status}"
+    exit 1
+    ;;
+esac
+
+exit 0

+ 7 - 0
frameworks/Python/API-Hour/hello/etc/logrotate.d/hello

@@ -0,0 +1,7 @@
+/var/log/hello/hello.log {
+    daily
+    rotate 15
+    compress
+    copytruncate
+    missingok
+}

+ 5 - 0
frameworks/Python/API-Hour/hello/etc/monit/conf.d/hello

@@ -0,0 +1,5 @@
+check process cache_updater with pidfile /run/lock/hello_0
+	group hello
+	start program = "/etc/init.d/hello start"
+	stop program = "/etc/init.d/hello stop"
+	if 5 restarts within 5 cycles then timeout

+ 126 - 0
frameworks/Python/API-Hour/hello/etc/rsyslog.conf

@@ -0,0 +1,126 @@
+#  /etc/rsyslog.conf	Configuration file for rsyslog.
+#
+#			For more information see
+#			/usr/share/doc/rsyslog-doc/html/rsyslog_conf.html
+
+
+#################
+#### MODULES ####
+#################
+
+$ModLoad imuxsock # provides support for local system logging
+$ModLoad imklog   # provides kernel logging support
+#$ModLoad immark  # provides --MARK-- message capability
+$SystemLogRateLimitInterval 1
+$SystemLogRateLimitBurst 1000
+
+# provides UDP syslog reception
+#$ModLoad imudp
+#$UDPServerRun 514
+
+# provides TCP syslog reception
+#$ModLoad imtcp
+#$InputTCPServerRun 514
+
+
+###########################
+#### GLOBAL DIRECTIVES ####
+###########################
+
+#
+# Use traditional timestamp format.
+# To enable high precision timestamps, comment out the following line.
+#
+$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
+
+#
+# Set the default permissions for all log files.
+#
+$FileOwner root
+$FileGroup adm
+$FileCreateMode 0640
+$DirCreateMode 0755
+$Umask 0022
+
+#
+# Where to place spool and state files
+#
+$WorkDirectory /var/spool/rsyslog
+
+#
+# Include all config files in /etc/rsyslog.d/
+#
+$IncludeConfig /etc/rsyslog.d/*.conf
+
+
+###############
+#### RULES ####
+###############
+
+#
+# First some standard log files.  Log by facility.
+#
+auth,authpriv.*			/var/log/auth.log
+*.*;auth,authpriv.none;\
+    local6.none                 -/var/log/syslog
+#cron.*				/var/log/cron.log
+daemon.*			-/var/log/daemon.log
+kern.*				-/var/log/kern.log
+lpr.*				-/var/log/lpr.log
+mail.*				-/var/log/mail.log
+user.*				-/var/log/user.log
+
+local6.*                        /var/log/hello/hello.log
+
+#
+# Logging for the mail system.  Split it up so that
+# it is easy to write scripts to parse these files.
+#
+mail.info			-/var/log/mail.info
+mail.warn			-/var/log/mail.warn
+mail.err			/var/log/mail.err
+
+#
+# Logging for INN news system.
+#
+news.crit			/var/log/news/news.crit
+news.err			/var/log/news/news.err
+news.notice			-/var/log/news/news.notice
+
+#
+# Some "catch-all" log files.
+#
+*.=debug;\
+	auth,authpriv.none;\
+	news.none;mail.none;local7.none	-/var/log/debug
+*.=info;*.=notice;*.=warn;\
+	auth,authpriv.none;\
+	cron,daemon.none;\
+	mail,news.none;local7.none		-/var/log/messages
+
+#
+# Emergencies are sent to everybody logged in.
+#
+*.emerg				:omusrmsg:*
+
+#
+# I like to have messages displayed on the console, but only on a virtual
+# console I usually leave idle.
+#
+#daemon,mail.*;\
+#	news.=crit;news.=err;news.=notice;\
+#	*.=debug;*.=info;\
+#	*.=notice;*.=warn	/dev/tty8
+
+# The named pipe /dev/xconsole is for the `xconsole' utility.  To use it,
+# you must invoke `xconsole' with the `-file' option:
+# 
+#    $ xconsole -file /dev/xconsole [...]
+#
+# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
+#      busy site..
+#
+daemon.*;mail.*;\
+	news.err;\
+	*.=debug;*.=info;\
+	*.=notice;*.=warn	|/dev/xconsole

+ 69 - 0
frameworks/Python/API-Hour/hello/hello/__init__.py

@@ -0,0 +1,69 @@
+import logging
+import asyncio
+import os
+
+import aiopg
+import jinja2
+import psycopg2.extras
+import aiohttp.web
+import aiohttp_jinja2
+import api_hour
+
+from . import endpoints
+
+
+LOG = logging.getLogger(__name__)
+
+
+class Container(api_hour.Container):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        # Servers
+        self.servers['http'] = aiohttp.web.Application(loop=kwargs['loop'])
+        aiohttp_jinja2.setup(self.servers['http'], loader=jinja2.PackageLoader('hello'))
+        self.servers['http'].ah_container = self # keep a reference to Container
+        # routes
+        self.servers['http'].router.add_route('GET', '/json', endpoints.world.json)
+        self.servers['http'].router.add_route('GET', '/db', endpoints.world.db)
+        self.servers['http'].router.add_route('GET', '/queries', endpoints.world.queries)
+        self.servers['http'].router.add_route('GET', '/fortunes', endpoints.world.fortunes)
+        self.servers['http'].router.add_route('GET', '/updates', endpoints.world.updates)
+        self.servers['http'].router.add_route('GET', '/plaintext', endpoints.world.plaintext)
+
+    def make_servers(self):
+        return [self.servers['http'].make_handler(logger=self.worker.log,
+                                                  debug=self.worker.cfg.debug,
+                                                  keep_alive=self.worker.cfg.keepalive,
+                                                  access_log=self.worker.log.access_log,
+                                                  access_log_format=self.worker.cfg.access_log_format)]
+
+
+    @asyncio.coroutine
+    def start(self):
+        yield from super().start()
+        LOG.info('Starting engines...')
+        self.engines['pg'] = self.loop.create_task(aiopg.create_pool(host=os.environ.get('DBHOST', self.config['engines']['pg']['host']),
+                                                                     port=int(self.config['engines']['pg']['port']),
+                                                                     sslmode='disable',
+                                                                     dbname=self.config['engines']['pg']['dbname'],
+                                                                     user=self.config['engines']['pg']['user'],
+                                                                     password=self.config['engines']['pg']['password'],
+                                                                     cursor_factory=psycopg2.extras.RealDictCursor,
+                                                                     minsize=int(self.config['engines']['pg']['minsize']),
+                                                                     maxsize=int(self.config['engines']['pg']['maxsize'])))
+        yield from asyncio.wait([self.engines['pg']], return_when=asyncio.ALL_COMPLETED)
+
+        LOG.info('All engines ready !')
+
+
+    @asyncio.coroutine
+    def stop(self):
+        LOG.info('Stopping engines...')
+        if 'pg' in self.engines:
+            if self.engines['pg'].done():
+                self.engines['pg'].result().terminate()
+                yield from self.engines['pg'].result().wait_closed()
+            else:
+                yield from self.engines['pg'].cancel()
+        LOG.info('All engines stopped !')
+        yield from super().stop()

+ 1 - 0
frameworks/Python/API-Hour/hello/hello/endpoints/__init__.py

@@ -0,0 +1 @@
+from . import world

+ 53 - 0
frameworks/Python/API-Hour/hello/hello/endpoints/world.py

@@ -0,0 +1,53 @@
+import logging
+import asyncio
+
+from aiohttp.web import Response
+from api_hour.plugins.aiohttp import JSON
+import aiohttp_jinja2
+
+from ..services import queries_number
+from ..services.world import get_random_record, get_random_records, update_random_records, get_fortunes
+
+LOG = logging.getLogger(__name__)
+
[email protected]
+def json(request):
+    """Test type 1: JSON serialization"""
+    return JSON({'message': 'Hello, World!'})
+
[email protected]
+def db(request):
+    """Test type 2: Single database query"""
+    container = request.app.ah_container
+
+    return JSON((yield from get_random_record(container)))
+
[email protected]
+def queries(request):
+    """Test type 3: Multiple database queries"""
+    container = request.app.ah_container
+    limit = queries_number(request.GET.get('queries', 1))
+
+    return JSON((yield from get_random_records(container, limit)))
+
[email protected]
+def fortunes(request):
+    """Test type 4: Fortunes"""
+    container = request.app.ah_container
+
+    return aiohttp_jinja2.render_template('fortunes.html.j2',
+                                          request,
+                                          {'fortunes': (yield from get_fortunes(container))})
+
[email protected]
+def updates(request):
+    """Test type 5: Database updates"""
+    container = request.app.ah_container
+    limit = queries_number(request.GET.get('queries', 1))
+
+    return JSON((yield from update_random_records(container, limit)))
+
[email protected]
+def plaintext(request):
+    """Test type 6: Plaintext"""
+    return Response(text='Hello, World!')

+ 14 - 0
frameworks/Python/API-Hour/hello/hello/services/__init__.py

@@ -0,0 +1,14 @@
+from . import world
+
+# get from Django hello application
+def queries_number(number):
+    try:
+        queries = int(number)
+    except Exception:
+        queries = 1
+    if queries < 1:
+        queries = 1
+    if queries > 500:
+        queries = 500
+    return queries
+

+ 60 - 0
frameworks/Python/API-Hour/hello/hello/services/world.py

@@ -0,0 +1,60 @@
+import asyncio
+from random import randint
+from operator import itemgetter
+
[email protected]
+def get_random_record(container):
+    pg = yield from container.engines['pg']
+
+    with (yield from pg.cursor()) as cur:
+            yield from cur.execute('SELECT id AS "Id", randomnumber AS "RandomNumber" FROM world WHERE id=%(idx)s LIMIT 1',
+                                   {'idx': randint(1, 10000)})
+            world = yield from cur.fetchone()
+    return world
+
[email protected]
+def get_random_records(container, limit):
+    tasks = []
+    results = []
+    for i in range(limit):
+        tasks.append(container.loop.create_task(get_random_record(container)))
+    yield from asyncio.wait(tasks)
+    for task in tasks:
+        results.append(task.result())
+    return results
+
[email protected]
+def update_random_record(container):
+    pg = yield from container.engines['pg']
+
+    world = yield from get_random_record(container)
+
+    with (yield from pg.cursor()) as cur:
+        yield from cur.execute('UPDATE world SET randomnumber=%(random_number)s WHERE id=%(idx)s',
+                               {'random_number': randint(1, 10000), 'idx': world['Id']})
+    return world
+
[email protected]
+def update_random_records(container, limit):
+    tasks = []
+    results = []
+    for i in range(limit):
+        tasks.append(container.loop.create_task(update_random_record(container)))
+    yield from asyncio.wait(tasks)
+    for task in tasks:
+        results.append(task.result())
+    return results
+
[email protected]
+def get_fortunes(container):
+    pg = yield from container.engines['pg']
+
+    with (yield from pg.cursor()) as cur:
+        yield from cur.execute('SELECT * FROM fortune')
+        fortunes = yield from cur.fetchall()
+
+    fortunes.append({'id': 0, 'message': 'Additional fortune added at request time.'})
+
+    fortunes.sort(key=itemgetter('message'))
+
+    return fortunes

+ 20 - 0
frameworks/Python/API-Hour/hello/hello/templates/fortunes.html.j2

@@ -0,0 +1,20 @@
+<!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['id']|e }}</td>
+            <td>{{ fortune['message']|e }}</td>
+        </tr>
+    {% endfor %}
+</table>
+</body>
+</html>

+ 3 - 0
frameworks/Python/API-Hour/hello/hello/utils/__init__.py

@@ -0,0 +1,3 @@
+import logging
+
+LOG = logging.getLogger(__name__)

+ 8 - 0
frameworks/Python/API-Hour/install.sh

@@ -0,0 +1,8 @@
+#!/bin/bash
+
+mkdir -p $IROOT/.pip_cache
+export PIP_DOWNLOAD_CACHE=$IROOT/.pip_cache
+
+fw_depends python3
+
+$IROOT/py3/bin/pip3 install --install-option="--prefix=${IROOT}/py3" -r $TROOT/requirements.txt

+ 11 - 0
frameworks/Python/API-Hour/requirements.txt

@@ -0,0 +1,11 @@
+api_hour==0.6.0
+aiopg==0.5.2
+gunicorn==19.1.1
+psycopg2==2.5.4
+setproctitle==1.1.8
+six==1.8.0
+ujson==1.33
+aiohttp==0.13.1
+PyYAML==3.11
+aiohttp-jinja2==0.0.2
+MarkupSafe==0.23

+ 3 - 0
frameworks/Python/API-Hour/setup.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+$PY3_API_HOUR -ac --chdir=hello/ --config_dir=hello/etc/hello/ hello:Container &

+ 9 - 0
frameworks/Python/API-Hour/source_code

@@ -0,0 +1,9 @@
+./API-Hour/etc/hello/main/main.yaml
+./API-Hour/etc/hello/api_hour/logging.ini
+./API-Hour/etc/hello/api_hour/gunicorn_conf.py
+./API-Hour/hello/__init__.py
+./API-Hour/hello/hello/endpoints/__init__.py
+./API-Hour/hello/hello/endpoints/world.py
+./API-Hour/hello/hello/services/__init__.py
+./API-Hour/hello/hello/services/world.py
+./API-Hour/hello/hello/utils/__init__.py

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


+ 0 - 0
frameworks/Python/django/hello/world/__init__.py


+ 23 - 0
frameworks/Python/pyramid/frameworkbenchmarks/__init__.py

@@ -0,0 +1,23 @@
+"""
+App config and initialization.
+"""
+
+from pyramid.request import Request
+from pyramid.config import Configurator
+from frameworkbenchmarks.models import sqlalchemy_encoder_factory
+
+
+def main(global_config, **settings):
+    """ This function returns a Pyramid WSGI application.
+    """
+    config = Configurator(settings=settings)
+    config.add_renderer('sqla_json', sqlalchemy_encoder_factory)
+    config.include('pyramid_chameleon')
+    config.add_route('test_1', '/json')
+    config.add_route('test_2', '/db')
+    config.add_route('test_3', '/queries')
+    config.add_route('test_4', '/fortunes')
+    config.add_route('test_5', '/updates')
+    config.add_route('test_6', '/plaintext')
+    config.scan()
+    return config.make_wsgi_app()

+ 4 - 4
frameworks/Python/pyramid/frameworkbenchmarks/views.py

@@ -21,7 +21,7 @@ def test_2(request):
     """
     Test type 2: Single database query
     """
-    num = randint(1, 10001)
+    num = randint(1, 10000)
     result = DBSession.query(World).filter(World.id == num).one()
     return result
 
@@ -43,7 +43,7 @@ def test_3(request):
             queries = 500
     result = [
         DBSession.query(World).filter(World.id == num).one()
-        for num in [randint(1, 10001) for _ in range(1, queries + 1)]
+        for num in [randint(1, 10000) for _ in range(1, queries + 1)]
     ]
     return result
 
@@ -77,10 +77,10 @@ def test_5(request):
             queries = 500
     objset = [
         DBSession.query(World).filter(World.id == num).one()
-        for num in [randint(1, 10001) for _ in range(1, queries + 1)]
+        for num in [randint(1, 10000) for _ in range(1, queries + 1)]
     ]
     for obj in objset:
-        obj.randomNumber = randint(1, 10001)
+        obj.randomNumber = randint(1, 10000)
     resultset = [obj.__json__() for obj in objset]
     DBSession.commit()
     return resultset

+ 6 - 5
frameworks/Scala/play-scala-mongodb/README.md

@@ -10,10 +10,11 @@ This is the Play portion of a [benchmarking test suite](../) comparing a variety
 The tests were run with:
 
 * [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
-* [Play 2.1.0](http://http://www.playframework.com/)
-* [Reactivemongo 0.9-SNAPSHOT](https://github.com/zenexity/Play-ReactiveMongo)
+* [Play 2.2.0](http://http://www.playframework.com/)
+* [Reactivemongo 0.10.5.0.akka22](https://github.com/zenexity/Play-ReactiveMongo)
 
 ## Test URLs
-### Data-Store/Database Mapping Test
-
-http://localhost/db?queries=5
+### Single Database Query test
+http://localhost:9000/db
+### Multiple Database Query test
+http://localhost:9000/queries?queries=5

+ 29 - 4
frameworks/Scala/play-scala-mongodb/app/controllers/Application.scala

@@ -32,21 +32,46 @@ object Application extends Controller {
 
   private def collection: JSONCollection = database.collection[JSONCollection]("world")
   private val projection = Json.obj("_id" -> 0)
+  /**
+   * Returns the closest number to <code>toRestrict</code> that is within the
+   * specified bounds, inclusive on both ends.
+   */
+  private def restrictWithin(toRestrict: String, lowerBound: Int, upperBound: Int): Option[Int] = {
+    try {
+      Some(math.min(upperBound, math.max(toRestrict.toInt, lowerBound)))
+    } catch {
+      case e: Exception => None
+    }
+  }
 
-  def db(queries: Int) = Action.async {
+  def dbqueries(requestedQueries: String) = Action.async {
     import scala.concurrent.ExecutionContext.Implicits.global
 
     val random = ThreadLocalRandom.current()
+    val queries = restrictWithin(requestedQueries, 1, 500).getOrElse(1)
     val futureWorlds = Future.sequence((for {
       _ <- 1 to queries
     } yield { collection
       .find(Json.obj("id" -> (random.nextInt(TestDatabaseRows) + 1)), projection)
       .one[JsValue]
     }))
-
     futureWorlds.map { worlds =>
-      Ok(Json.toJson(worlds))
+      Ok(Json.toJson(worlds.map {maybeWorld =>
+        maybeWorld.map {world =>
+          world.as[Map[String, Int]]
+        }
+      }))
     }
   }
+  def singledb() = Action.async {
+    import scala.concurrent.ExecutionContext.Implicits.global
 
-}
+    val random = ThreadLocalRandom.current()
+    val futureWorld = collection
+      .find(Json.obj("id" -> (random.nextInt(TestDatabaseRows) + 1)), projection)
+      .one[JsValue]
+    futureWorld.map { world =>
+      Ok(Json.toJson(world.head.as[Map[String, Int]]))
+    }
+  }
+}

+ 1 - 1
frameworks/Scala/play-scala-mongodb/benchmark_config

@@ -4,7 +4,7 @@
     "default": {
       "setup_file": "setup",
       "db_url": "/db",
-      "query_url": "/db?queries=",
+      "query_url": "/queries?queries=",
       "port": 9000,
       "approach": "Realistic",
       "classification": "Fullstack",

+ 2 - 1
frameworks/Scala/play-scala-mongodb/conf/routes

@@ -3,7 +3,8 @@
 # ~~~~
 
 # Home page
-GET     /db                             controllers.Application.db(queries: Int ?= 1)
+GET     /db                             controllers.Application.singledb()
+GET     /queries                        controllers.Application.dbqueries(queries: String ?= "1")
 
 # Map static resources from the /public folder to the /assets URL path
 GET     /assets/*file                   controllers.Assets.at(path="/public", file)

+ 1 - 1
frameworks/Scala/play-scala-mongodb/project/Build.scala

@@ -7,7 +7,7 @@ object ApplicationBuild extends Build {
   val appVersion      = "1.0-SNAPSHOT"
 
   val appDependencies = Seq(
-    "org.reactivemongo" %% "play2-reactivemongo" % "0.9" exclude("org.scala-stm", "scala-stm_2.10.0")
+    "org.reactivemongo" %% "play2-reactivemongo" % "0.10.5.0.akka22" exclude("org.scala-stm", "scala-stm_2.10.0")
   )
 
   val main = play.Project(appName, appVersion, appDependencies).settings(

+ 1 - 0
toolset/setup/linux/languages/python3.sh

@@ -10,6 +10,7 @@ cd Python-3.4.2
 make -j4 --quiet
 make install --quiet
 
+ln -s ${IROOT}/py3/bin/python3.4m ${IROOT}/py3/bin/python3.4
 ${IROOT}/py3/bin/python3 -m ensurepip -U
 ${IROOT}/py3/bin/pip3 install -U setuptools pip