Browse Source

Merge branch 'TechEmpower:master' into master

Jeremy Kuhn 1 year ago
parent
commit
8d543cdaec
100 changed files with 2546 additions and 532 deletions
  1. 1 1
      .github/PULL_REQUEST_TEMPLATE.md
  2. 42 19
      .github/workflows/build.yml
  3. 37 0
      .github/workflows/get-maintainers.yml
  4. 1 1
      .github/workflows/label-failing-pr.yml
  5. 49 0
      .github/workflows/ping-maintainers.yml
  6. 7 3
      .gitignore
  7. 51 17
      Dockerfile
  8. 2 1
      README.md
  9. 7 0
      entrypoint.sh
  10. 2 1
      frameworks/C++/cpoll_cppsp/benchmark_config.json
  11. 0 2
      frameworks/C++/cppcms/benchmark_config.json
  12. 2 1
      frameworks/C++/cppcms/config-nginx-mysql.json
  13. 2 1
      frameworks/C++/cppcms/config-nginx-postgresql.json
  14. 6 15
      frameworks/C++/cppcms/nginx.conf
  15. 27 25
      frameworks/C++/cutelyst/benchmark_config.json
  16. 14 13
      frameworks/C++/cutelyst/build.sh
  17. 1 1
      frameworks/C++/cutelyst/cutelyst-nginx-my.dockerfile
  18. 1 1
      frameworks/C++/cutelyst/cutelyst-nginx-pg.dockerfile
  19. 1 1
      frameworks/C++/cutelyst/cutelyst-nginx.dockerfile
  20. 1 1
      frameworks/C++/cutelyst/cutelyst-pf-apg-batch.dockerfile
  21. 1 1
      frameworks/C++/cutelyst/cutelyst-pf-apg.dockerfile
  22. 1 1
      frameworks/C++/cutelyst/cutelyst-pf-my.dockerfile
  23. 1 1
      frameworks/C++/cutelyst/cutelyst-pf-pg.dockerfile
  24. 1 1
      frameworks/C++/cutelyst/cutelyst-t-apg-cutelee.dockerfile
  25. 1 1
      frameworks/C++/cutelyst/cutelyst-thread-apg-batch.dockerfile
  26. 1 1
      frameworks/C++/cutelyst/cutelyst-thread-apg.dockerfile
  27. 1 1
      frameworks/C++/cutelyst/cutelyst-thread-my-cutelee.dockerfile
  28. 1 1
      frameworks/C++/cutelyst/cutelyst-thread-my.dockerfile
  29. 1 1
      frameworks/C++/cutelyst/cutelyst-thread-pg-cutelee.dockerfile
  30. 1 1
      frameworks/C++/cutelyst/cutelyst-thread-pg.dockerfile
  31. 1 1
      frameworks/C++/cutelyst/cutelyst-thread-pico.dockerfile
  32. 1 1
      frameworks/C++/cutelyst/cutelyst-thread.dockerfile
  33. 1 1
      frameworks/C++/cutelyst/cutelyst.dockerfile
  34. 16 14
      frameworks/C++/cutelyst/src/CMakeLists.txt
  35. 70 0
      frameworks/C++/cutelyst/src/cachedqueries.cpp
  36. 19 0
      frameworks/C++/cutelyst/src/cachedqueries.h
  37. 31 27
      frameworks/C++/cutelyst/src/cutelyst-benchmarks.cpp
  38. 55 54
      frameworks/C++/cutelyst/src/databaseupdatestest.cpp
  39. 4 4
      frameworks/C++/cutelyst/src/databaseupdatestest.h
  40. 29 27
      frameworks/C++/cutelyst/src/fortunetest.cpp
  41. 6 6
      frameworks/C++/cutelyst/src/fortunetest.h
  42. 1 1
      frameworks/C++/cutelyst/src/jsontest.cpp
  43. 18 16
      frameworks/C++/cutelyst/src/multipledatabasequeriestest.cpp
  44. 3 3
      frameworks/C++/cutelyst/src/multipledatabasequeriestest.h
  45. 2 2
      frameworks/C++/cutelyst/src/plaintexttest.cpp
  46. 0 37
      frameworks/C++/cutelyst/src/root.cpp
  47. 0 22
      frameworks/C++/cutelyst/src/root.h
  48. 46 13
      frameworks/C++/cutelyst/src/singledatabasequerytest.cpp
  49. 7 4
      frameworks/C++/cutelyst/src/singledatabasequerytest.h
  50. 14 18
      frameworks/C++/drogon/drogon-core.dockerfile
  51. 14 18
      frameworks/C++/drogon/drogon.dockerfile
  52. 2 1
      frameworks/C++/drogon/drogon_benchmark/config-core.json
  53. 3 2
      frameworks/C++/drogon/drogon_benchmark/config.json
  54. 1 2
      frameworks/C++/drogon/drogon_benchmark/plugins/SyncPlugin.cc
  55. 7 7
      frameworks/C++/ffead-cpp/benchmark_config.json
  56. 38 0
      frameworks/C++/just-boost/README.md
  57. 27 0
      frameworks/C++/just-boost/benchmark_config.json
  58. 25 0
      frameworks/C++/just-boost/just-boost.dockerfile
  59. 401 0
      frameworks/C++/just-boost/main.cpp
  60. 0 48
      frameworks/C++/lithium/benchmark_config.json
  61. 3 1
      frameworks/C++/poco/poco.dockerfile
  62. 9 0
      frameworks/C++/reactor/README.md
  63. 23 0
      frameworks/C++/reactor/benchmark_config.json
  64. 3 5
      frameworks/C++/reactor/config.toml
  65. 37 0
      frameworks/C++/reactor/reactor.dockerfile
  66. 120 0
      frameworks/C++/reactor/techempower.cpp
  67. 6 6
      frameworks/C++/suil/benchmark/src/main.cpp
  68. 1 1
      frameworks/C++/suil/suil.dockerfile
  69. 0 4
      frameworks/C++/treefrog/benchmark_config.json
  70. 47 15
      frameworks/C++/treefrog/config/application.ini
  71. 10 0
      frameworks/C++/treefrog/config/cache.ini
  72. 34 2
      frameworks/C++/treefrog/controllers/worldcontroller.cpp
  73. 10 10
      frameworks/C++/treefrog/treefrog-epoll.dockerfile
  74. 9 9
      frameworks/C++/treefrog/treefrog-mongodb.dockerfile
  75. 9 9
      frameworks/C++/treefrog/treefrog-mysql.dockerfile
  76. 9 9
      frameworks/C++/treefrog/treefrog.dockerfile
  77. 16 8
      frameworks/C++/ulib/benchmark_config.json
  78. 42 0
      frameworks/C++/userver/README.md
  79. 55 0
      frameworks/C++/userver/benchmark_config.json
  80. 14 10
      frameworks/C++/userver/config.toml
  81. 27 0
      frameworks/C++/userver/userver-bare.dockerfile
  82. 27 0
      frameworks/C++/userver/userver.dockerfile
  83. 3 0
      frameworks/C++/userver/userver_benchmark/.clang-format
  84. 18 0
      frameworks/C++/userver/userver_benchmark/CMakeLists.txt
  85. 205 0
      frameworks/C++/userver/userver_benchmark/bare/simple_connection.cpp
  86. 26 0
      frameworks/C++/userver/userver_benchmark/bare/simple_connection.hpp
  87. 12 0
      frameworks/C++/userver/userver_benchmark/bare/simple_response.hpp
  88. 90 0
      frameworks/C++/userver/userver_benchmark/bare/simple_router.cpp
  89. 46 0
      frameworks/C++/userver/userver_benchmark/bare/simple_router.hpp
  90. 28 0
      frameworks/C++/userver/userver_benchmark/bare/simple_server.cpp
  91. 34 0
      frameworks/C++/userver/userver_benchmark/bare/simple_server.hpp
  92. 80 0
      frameworks/C++/userver/userver_benchmark/common/db_helpers.cpp
  93. 50 0
      frameworks/C++/userver/userver_benchmark/common/db_helpers.hpp
  94. 37 0
      frameworks/C++/userver/userver_benchmark/controllers/cached_queries/handler.cpp
  95. 29 0
      frameworks/C++/userver/userver_benchmark/controllers/cached_queries/handler.hpp
  96. 21 0
      frameworks/C++/userver/userver_benchmark/controllers/cached_queries/world_cache_component.cpp
  97. 36 0
      frameworks/C++/userver/userver_benchmark/controllers/cached_queries/world_cache_component.hpp
  98. 166 0
      frameworks/C++/userver/userver_benchmark/controllers/fortunes/handler.cpp
  99. 32 0
      frameworks/C++/userver/userver_benchmark/controllers/fortunes/handler.hpp
  100. 16 0
      frameworks/C++/userver/userver_benchmark/controllers/json/handler.cpp

+ 1 - 1
.github/PULL_REQUEST_TEMPLATE.md

@@ -1,7 +1,7 @@
 <!--
 <!--
 Thank you for submitting to the TechEmpower Framework Benchmarks!
 Thank you for submitting to the TechEmpower Framework Benchmarks!
 
 
-If you are submitting a new framework, please make sure that you add the appropriate line in the `.github/workflows/build.yml` file for proper integration testing. Also please make sure that an appropriate `README.md` is added in your framework directory with information about the framework and a link to its homepage and documentation.
+If you are submitting a new framework, please make sure that an appropriate `README.md` is added in your framework directory with information about the framework and a link to its homepage and documentation.
 
 
 For new frameworks, please do not include source code that isn't required for the benchmarks.
 For new frameworks, please do not include source code that isn't required for the benchmarks.
 
 

+ 42 - 19
.github/workflows/build.yml

@@ -2,7 +2,7 @@ name: build
 on: [ push, pull_request ]
 on: [ push, pull_request ]
 jobs:
 jobs:
   setup:
   setup:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
     steps:
     steps:
       # Required for workflow triggers like the auto-label for failing PRs
       # Required for workflow triggers like the auto-label for failing PRs
       - name: Save PR number
       - name: Save PR number
@@ -20,7 +20,7 @@ jobs:
       #
       #
       # We need to fetch more than one commit to be able to access HEAD^2 in case
       # We need to fetch more than one commit to be able to access HEAD^2 in case
       # of a pull request
       # of a pull request
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
         with:
         with:
           fetch-depth: 10
           fetch-depth: 10
       # In case of a push event, the commit we care about is simply HEAD.
       # In case of a push event, the commit we care about is simply HEAD.
@@ -46,9 +46,9 @@ jobs:
           echo "$(git log --format=%B -n 1 HEAD^2)" >> $GITHUB_ENV
           echo "$(git log --format=%B -n 1 HEAD^2)" >> $GITHUB_ENV
           echo "EOF" >> $GITHUB_ENV
           echo "EOF" >> $GITHUB_ENV
           echo "PREVIOUS_COMMIT=$(git log --format=%H -n 1 HEAD^2~1)" >> $GITHUB_ENV
           echo "PREVIOUS_COMMIT=$(git log --format=%H -n 1 HEAD^2~1)" >> $GITHUB_ENV
-      - uses: actions/setup-python@v2
+      - uses: actions/setup-python@v4
         with:
         with:
-          python-version: '2.7'
+          python-version: '3.10'
           architecture: 'x64'
           architecture: 'x64'
       - name: Get all changes vs master
       - name: Get all changes vs master
         env:
         env:
@@ -65,14 +65,16 @@ jobs:
       - id: event_out
       - id: event_out
         name: Write event outputs
         name: Write event outputs
         run: |
         run: |
-          # Escape the multiline string for Github Actions, see https://github.community/t/set-output-truncates-multiline-strings/16852/3
-          COMMIT_MESSAGE="${COMMIT_MESSAGE//'%'/'%25'}"
-          COMMIT_MESSAGE="${COMMIT_MESSAGE//$'\n'/'%0A'}"
-          COMMIT_MESSAGE="${COMMIT_MESSAGE//$'\r'/'%0D'}"
-          echo "::set-output name=commit_message::$COMMIT_MESSAGE"
-          echo "::set-output name=branch_name::$BRANCH_NAME"
-          echo "::set-output name=target_branch_name::$TARGET_BRANCH_NAME"
-          echo "::set-output name=previous_commit::$PREVIOUS_COMMIT"
+          # Escape the multiline string for Github Actions, see https://lab.amalitsky.com/posts/2022/github-actions-set-output-migration/#migration-from-set-output-to-github-output
+          delimiter="$(openssl rand -hex 8)"
+              {
+                echo "commit_message<<${delimiter}"
+                echo $COMMIT_MESSAGE
+                echo "${delimiter}"
+              } >> $GITHUB_OUTPUT
+          echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
+          echo "target_branch_name=$TARGET_BRANCH_NAME" >> $GITHUB_OUTPUT
+          echo "previous_commit=$PREVIOUS_COMMIT" >> $GITHUB_OUTPUT
       - id: verify_out
       - id: verify_out
         name: Write verify job matrix
         name: Write verify job matrix
         run: |
         run: |
@@ -82,7 +84,7 @@ jobs:
           #   - TESTLANG: {lang}
           #   - TESTLANG: {lang}
           # with a TESTLANG object in the include array for each language under frameworks
           # with a TESTLANG object in the include array for each language under frameworks
           VERIFY_MATRIX=$(ls -1 frameworks | jq -Rc '.+"/" | select(inside(env.RUN_TESTS))  | rtrimstr("/")' | jq -sc '{include: map({TESTLANG: .})}')
           VERIFY_MATRIX=$(ls -1 frameworks | jq -Rc '.+"/" | select(inside(env.RUN_TESTS))  | rtrimstr("/")' | jq -sc '{include: map({TESTLANG: .})}')
-          echo "::set-output name=verify_matrix::$VERIFY_MATRIX"
+          echo "verify_matrix=$VERIFY_MATRIX" >> $GITHUB_OUTPUT
     outputs:
     outputs:
       commit_message: ${{ steps.event_out.outputs.commit_message }}
       commit_message: ${{ steps.event_out.outputs.commit_message }}
       branch_name: ${{ steps.event_out.outputs.branch_name }}
       branch_name: ${{ steps.event_out.outputs.branch_name }}
@@ -93,7 +95,7 @@ jobs:
     needs: setup
     needs: setup
     # The matrix check is necessary because an empty job matrix is otherwise considered a workflow failure
     # The matrix check is necessary because an empty job matrix is otherwise considered a workflow failure
     if: ${{ !contains(needs.setup.outputs.commit_message, '[ci skip]') && contains(needs.setup.outputs.verify_matrix, 'TESTLANG') }}
     if: ${{ !contains(needs.setup.outputs.commit_message, '[ci skip]') && contains(needs.setup.outputs.verify_matrix, 'TESTLANG') }}
-    runs-on: ubuntu-18.04
+    runs-on: ubuntu-22.04
     strategy:
     strategy:
       matrix: ${{ fromJSON(needs.setup.outputs.verify_matrix) }}
       matrix: ${{ fromJSON(needs.setup.outputs.verify_matrix) }}
       # Disable fail-fast to allow all failing frameworks/etc to fail in a
       # Disable fail-fast to allow all failing frameworks/etc to fail in a
@@ -108,12 +110,12 @@ jobs:
       PREVIOUS_COMMIT: ${{ needs.setup.outputs.previous_commit }}
       PREVIOUS_COMMIT: ${{ needs.setup.outputs.previous_commit }}
       PR_NUMBER: ${{ github.event.pull_request.number }}
       PR_NUMBER: ${{ github.event.pull_request.number }}
     steps:
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v3
         with:
         with:
           fetch-depth: 10
           fetch-depth: 10
-      - uses: actions/setup-python@v2
+      - uses: actions/setup-python@v4
         with:
         with:
-          python-version: '2.7'
+          python-version: '3.10'
           architecture: 'x64'
           architecture: 'x64'
       - name: Get all changes vs master
       - name: Get all changes vs master
         # Runs github_actions_diff, with the the output accessible in later steps
         # Runs github_actions_diff, with the the output accessible in later steps
@@ -138,7 +140,10 @@ jobs:
           if [ "$RUN_TESTS" ]; then echo "Proceeding to run tests."; else echo 'Skipping test verification.'; fi
           if [ "$RUN_TESTS" ]; then echo "Proceeding to run tests."; else echo 'Skipping test verification.'; fi
       - name: Build tfb dockerfile
       - name: Build tfb dockerfile
         if: ${{ env.RUN_TESTS }}
         if: ${{ env.RUN_TESTS }}
-        run: docker build -t techempower/tfb - < ./Dockerfile;
+        uses: mattes/cached-docker-build-action@v1
+        with:
+          args: " --file ./Dockerfile --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) --tag techempower/tfb ."
+          cache_key: "${{ hashFiles('./Dockerfile') }}"
       - name: Stop services
       - name: Stop services
         # Stop services that would claim ports we may need
         # Stop services that would claim ports we may need
         run: |
         run: |
@@ -150,4 +155,22 @@ jobs:
           # run-ci.py runs the diffing to see if github actions needs to test this framework. Ideally/eventually,
           # run-ci.py runs the diffing to see if github actions needs to test this framework. Ideally/eventually,
           # we'd like to try and do the diffing before github_actions_clean & setup.
           # we'd like to try and do the diffing before github_actions_clean & setup.
           # This will run the tests exactly as you would in your own vm:
           # This will run the tests exactly as you would in your own vm:
-          docker network create tfb > /dev/null 2>&1 && docker run --network=tfb -v /var/run/docker.sock:/var/run/docker.sock --mount type=bind,source=`pwd`,target=/FrameworkBenchmarks techempower/tfb --mode verify --test-dir $RUN_TESTS --results-environment Github-Actions;
+          docker network create tfb > /dev/null 2>&1 && docker run --network=tfb -e USER_ID=$(id -u) -v /var/run/docker.sock:/var/run/docker.sock --mount type=bind,source=`pwd`,target=/FrameworkBenchmarks techempower/tfb --mode verify --test-dir $RUN_TESTS --results-environment Github-Actions;
+  dependabot:
+    needs: verify
+    runs-on: ubuntu-latest
+    permissions:
+      pull-requests: write
+      contents: write
+    if: ${{ github.actor == 'dependabot[bot]' }}
+    steps:
+      - name: Dependabot metadata
+        id: metadata
+        uses: dependabot/[email protected]
+        with:
+          github-token: "${{ secrets.GITHUB_TOKEN }}"
+      - name: Enable auto-merge for Dependabot PRs
+        run: gh pr merge --auto --merge "$PR_URL"
+        env:
+          PR_URL: ${{github.event.pull_request.html_url}}
+          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

+ 37 - 0
.github/workflows/get-maintainers.yml

@@ -0,0 +1,37 @@
+name: get-maintainers
+on: 
+  pull_request:
+    types: [opened, reopened]
+permissions:
+  pull-requests: write
+jobs:
+  get_maintainers:
+    runs-on: ubuntu-22.04
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 10
+      - name: Get commit branch and commit message from PR
+        run: |
+          echo "BRANCH_NAME=$GITHUB_HEAD_REF" >> $GITHUB_ENV
+          echo "TARGET_BRANCH_NAME=$(echo ${GITHUB_BASE_REF##*/})" >> $GITHUB_ENV
+          echo "COMMIT_MESSAGE<<EOF" >> $GITHUB_ENV
+          echo "$(git log --format=%B -n 1 HEAD^2)" >> $GITHUB_ENV
+          echo "EOF" >> $GITHUB_ENV
+          echo "PREVIOUS_COMMIT=$(git log --format=%H -n 1 HEAD^2~1)" >> $GITHUB_ENV
+      - uses: actions/setup-python@v4
+        with:
+          python-version: '3.10'
+          architecture: 'x64'
+      - name: Save PR number
+        run: |
+          mkdir -p ./maintainers
+          echo ${{ github.event.number }} > ./maintainers/NR
+      - name: Get Maintainers
+        run: |
+          python ./toolset/github_actions/get_maintainers.py > ./maintainers/maintainers.md
+      - name: Save Maintainers
+        uses: actions/upload-artifact@v3
+        with:
+          name: maintainers
+          path: maintainers/

+ 1 - 1
.github/workflows/label-failing-pr.yml

@@ -7,7 +7,7 @@ on:
 jobs:
 jobs:
   apply_label:
   apply_label:
     if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'failure' }}
     if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'failure' }}
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
     steps:
     steps:
       - name: 'Download artifact'
       - name: 'Download artifact'
         uses: actions/[email protected]
         uses: actions/[email protected]

+ 49 - 0
.github/workflows/ping-maintainers.yml

@@ -0,0 +1,49 @@
+name: Ping Maintainers
+on:
+  workflow_run:
+    workflows: [ "get-maintainers"]
+    types:
+      - completed
+permissions:
+  pull-requests: write
+jobs:
+  ping_maintainers:
+    runs-on: ubuntu-22.04
+    steps:
+      - name: 'Download maintainers artifact'
+        uses: actions/github-script@v6
+        with:
+          script: |
+            let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
+               owner: context.repo.owner,
+               repo: context.repo.repo,
+               run_id: ${{github.event.workflow_run.id }},
+            });
+            let matchArtifact = artifacts.data.artifacts.filter((artifact) => {
+              return artifact.name == "maintainers"
+            })[0];
+            let download = await github.rest.actions.downloadArtifact({
+               owner: context.repo.owner,
+               repo: context.repo.repo,
+               artifact_id: matchArtifact.id,
+               archive_format: 'zip',
+            });
+            let fs = require('fs');
+            fs.writeFileSync('${{github.workspace}}/maintainers.zip', Buffer.from(download.data));
+      - run: unzip maintainers.zip
+      - name: Ping maintainers
+        uses: actions/github-script@v6
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          script: |
+            let fs = require('fs');
+            let issue_number = Number(fs.readFileSync('./NR'));
+            let maintainers_comment = fs.readFileSync('./maintainers.md', 'utf8');
+            if (maintainers_comment) {
+              await github.rest.issues.createComment({
+                issue_number: issue_number,
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                body: maintainers_comment
+              });
+            }

+ 7 - 3
.gitignore

@@ -1,6 +1,4 @@
-.DS_Store
 installs
 installs
-*.log
 node_modules/
 node_modules/
 .travis.bak
 .travis.bak
 
 
@@ -35,6 +33,7 @@ build/
 *.patch
 *.patch
 */bin/
 */bin/
 
 
+
 # intellij
 # intellij
 *.iml
 *.iml
 *.ipr
 *.ipr
@@ -59,9 +58,14 @@ benchmark.cfg
 
 
 # Visual Studio Code
 # Visual Studio Code
 .vscode
 .vscode
+.history/
+.devcontainer
 
 
 # vim
 # vim
 .*.sw[a-p]
 .*.sw[a-p]
 
 
 # merge tooling
 # merge tooling
-*.orig
+*.orig
+
+# python
+.venv/

+ 51 - 17
Dockerfile

@@ -1,26 +1,60 @@
-FROM buildpack-deps:bionic
+FROM ubuntu:22.04
 
 
+ARG DEBIAN_FRONTEND=noninteractive
+# WARNING: DON'T PUT A SPACE AFTER ANY BACKSLASH OR APT WILL BREAK
 # One -q produces output suitable for logging (mostly hides
 # One -q produces output suitable for logging (mostly hides
 # progress indicators)
 # progress indicators)
-RUN apt-get -yqq update
+RUN apt-get -yqq update && \
+    apt-get -yqq install \
+      -o Dpkg::Options::="--force-confdef" \
+      -o Dpkg::Options::="--force-confold" \
+      cloc \
+      curl \
+      gcc \
+      git-core \
+      gosu \
+      # Needed for mysqlclient
+      libmysqlclient-dev \
+      libpq-dev \
+      pkg-config \
+      python3 \
+      python3-dev \
+      python3-pip \
+      siege \
+      software-properties-common
 
 
-# WARNING: DONT PUT A SPACE AFTER ANY BACKSLASH OR APT WILL BREAK
-RUN apt-get -yqq install -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" \
-  git-core \
-  cloc dstat                    `# Collect resource usage statistics` \
-  python-dev \
-  python-pip \
-  software-properties-common \
-  libmysqlclient-dev            `# Needed for MySQL-python`
+RUN pip3 install \
+      colorama==0.3.1 \
+      docker==4.0.2 \
+      mysqlclient \
+      psutil \
+      psycopg2-binary \
+      pymongo==3.13.0 \
+      # urllib3 incompatibility:
+      # https://github.com/docker/docker-py/issues/3113#issuecomment-1525500104
+      requests==2.28.1
 
 
-RUN pip install colorama==0.3.1 requests MySQL-python psycopg2-binary pymongo docker==4.0.2 psutil
+# Collect resource usage statistics
+ARG DOOL_VERSION=v1.2.0
 
 
-RUN apt-get install -yqq siege
+WORKDIR /tmp
+RUN curl -LSs "https://github.com/scottchiefbaker/dool/archive/${DOOL_VERSION}.tar.gz" | \
+      tar --strip-components=1 -xz && \
+    ./install.py
 
 
-# Fix for docker-py trying to import one package from the wrong location
-RUN cp -r /usr/local/lib/python2.7/dist-packages/backports/ssl_match_hostname/ /usr/lib/python2.7/dist-packages/backports
+# Check if the group ID is already created
+ARG GROUP_ID
+RUN if ! getent group "$GROUP_ID"; then \
+      addgroup --gid "$GROUP_ID" user; \
+    fi
 
 
-ENV PYTHONPATH /FrameworkBenchmarks
-ENV FWROOT /FrameworkBenchmarks
+# Check if the user ID is already created
+ARG USER_ID
+RUN if ! getent passwd "$USER_ID"; then \
+      adduser --disabled-password --gecos '' --gid "$GROUP_ID" --uid "$USER_ID" user; \
+    fi
 
 
-ENTRYPOINT ["python", "/FrameworkBenchmarks/toolset/run-tests.py"]
+ENV FWROOT=/FrameworkBenchmarks USER_ID="$USER_ID"
+ENV PYTHONPATH="$FWROOT"
+
+ENTRYPOINT ["/FrameworkBenchmarks/entrypoint.sh"]

+ 2 - 1
README.md

@@ -89,7 +89,8 @@ Results of continuous benchmarking runs are available in real time [here](https:
 
 
 ### Data Visualization
 ### Data Visualization
 
 
-If you have a `results.json` file that you would like to visualize, you can [do that here](https://tfb-status.techempower.com/share). You can also attach a `runid` parameter to that url where `runid` is a run listed on [tfb-status](https://tfb-status.techempower.com) like so: https://www.techempower.com/benchmarks/#section=test&runid=fd07b64e-47ce-411e-8b9b-b13368e988c6
+If you have a `results.json` file that you would like to visualize, you can [do that here](https://tfb-status.techempower.com/share). You can also attach a `runid` parameter to that url where `runid` is a run listed on [tfb-status](https://tfb-status.techempower.com) like so: https://www.techempower.com/benchmarks/#section=test&runid=fd07b64e-47ce-411e-8b9b-b13368e988c6.
+If you want to visualize them or compare different results files on bash, here is an unofficial [plaintext results parser](https://github.com/joeyleeeeeee97/PlainTextResultsParser)
 
 
 ## Contributing
 ## Contributing
 
 

+ 7 - 0
entrypoint.sh

@@ -0,0 +1,7 @@
+#!/bin/bash
+
+set -eo pipefail -u
+
+chown -LR "$USER_ID" /var/run
+# Drop permissions of user to match those of the host system
+gosu "$USER_ID" python3 "${FWROOT}/toolset/run-tests.py" "$@"

+ 2 - 1
frameworks/C++/cpoll_cppsp/benchmark_config.json

@@ -38,7 +38,8 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "cpoll-cppsp-raw",
       "display_name": "cpoll-cppsp-raw",
       "notes": "",
       "notes": "",
-      "versus": "cpoll_cppsp"
+      "versus": "cpoll_cppsp",
+      "tags": ["broken"]
     },
     },
     "postgres-raw": {
     "postgres-raw": {
       "db_url": "/db_pg_async",
       "db_url": "/db_pg_async",

+ 0 - 2
frameworks/C++/cppcms/benchmark_config.json

@@ -24,10 +24,8 @@
       "notes": ""
       "notes": ""
     },
     },
     "postgres": {
     "postgres": {
-      "json_url": "/json",
       "db_url": "/db",
       "db_url": "/db",
       "fortune_url": "/fortunes",
       "fortune_url": "/fortunes",
-      "plaintext_url": "/plaintext",
       "cached_query_url": "/cached-worlds/",
       "cached_query_url": "/cached-worlds/",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",

+ 2 - 1
frameworks/C++/cppcms/config-nginx-mysql.json

@@ -4,7 +4,8 @@
     },
     },
     "service": {
     "service": {
         "api": "fastcgi",
         "api": "fastcgi",
-        "socket": "/var/tmp/cppcms.sock"
+        "socket": "/var/tmp/cppcms.sock",
+        "backlog": 65535
     },
     },
     "http": {
     "http": {
         "script": "/"
         "script": "/"

+ 2 - 1
frameworks/C++/cppcms/config-nginx-postgresql.json

@@ -4,7 +4,8 @@
     },
     },
     "service": {
     "service": {
         "api": "fastcgi",
         "api": "fastcgi",
-        "socket": "/var/tmp/cppcms.sock"
+        "socket": "/var/tmp/cppcms.sock",
+        "backlog": 65535
     },
     },
     "http": {
     "http": {
         "script": "/"
         "script": "/"

+ 6 - 15
frameworks/C++/cppcms/nginx.conf

@@ -7,32 +7,23 @@ error_log stderr error;
 events {
 events {
     # This needed to be increased because the nginx error log said so.
     # This needed to be increased because the nginx error log said so.
     # http://nginx.org/en/docs/ngx_core_module.html#worker_connections
     # http://nginx.org/en/docs/ngx_core_module.html#worker_connections
-    worker_connections  65535;
+    worker_connections 65535;
     multi_accept on;
     multi_accept on;
 }
 }
 
 
 http {
 http {
     default_type  application/octet-stream;
     default_type  application/octet-stream;
     client_body_temp_path      /tmp;
     client_body_temp_path      /tmp;
-
-    # turn off request logging for performance
     access_log off;
     access_log off;
+    server_tokens off;
 
 
-    # I think these only options affect static file serving
-    sendfile        on;
-    tcp_nopush      on;
+    sendfile on;
+    tcp_nopush on;
 
 
-    # Allow many HTTP Keep-Alive requests in a single TCP connection before
-    # closing it (the default is 100). This will minimize the total number
-    # of TCP connections opened/closed. The problem is that this may cause
-    # some worker processes to be handling too connections relative to the
-    # other workers based on an initial imbalance, so this is disabled for
-    # now.
+    keepalive_timeout 65;
+    keepalive_disable none;
     keepalive_requests 1000;
     keepalive_requests 1000;
 
 
-    #keepalive_timeout  0;
-    keepalive_timeout  65;
-
     server {
     server {
         # For information on deferred, see:
         # For information on deferred, see:
         # http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
         # http://nginx.org/en/docs/http/ngx_http_core_module.html#listen

+ 27 - 25
frameworks/C++/cutelyst/benchmark_config.json

@@ -20,10 +20,11 @@
                 "versus": ""
                 "versus": ""
             },
             },
             "pf-apg": {
             "pf-apg": {
-                "db_url": "/dbp",
-                "query_url": "/queriesp?queries=",
+                "db_url": "/pg",
+                "query_url": "/querAPG?queries=",
                 "update_url": "/updatep?queries=",
                 "update_url": "/updatep?queries=",
-                "fortune_url": "/fortunes_raw_p",
+                "fortune_url": "/f_RW_APG",
+                "cached_query_url": "/cached_queries?count=",
                 "port": 8080,
                 "port": 8080,
                 "approach": "Realistic",
                 "approach": "Realistic",
                 "classification": "Fullstack",
                 "classification": "Fullstack",
@@ -57,10 +58,10 @@
                 "versus": ""
                 "versus": ""
             },
             },
             "pf-pg": {
             "pf-pg": {
-                "db_url": "/db_postgres",
-                "query_url": "/query_postgres?queries=",
-                "update_url": "/updates_postgres?queries=",
-                "fortune_url": "/fortunes_raw_postgres",
+                "db_url": "/PG",
+                "query_url": "/queryPG?queries=",
+                "update_url": "/ups_QPG?queries=",
+                "fortune_url": "/f_RW_QPG",
                 "port": 8080,
                 "port": 8080,
                 "approach": "Realistic",
                 "approach": "Realistic",
                 "classification": "Fullstack",
                 "classification": "Fullstack",
@@ -77,10 +78,10 @@
                 "versus": ""
                 "versus": ""
             },
             },
             "pf-my": {
             "pf-my": {
-                "db_url": "/db_mysql",
-                "query_url": "/query_mysql?queries=",
-                "update_url": "/updates_mysql?queries=",
-                "fortune_url": "/fortunes_raw_mysql",
+                "db_url": "/MY",
+                "query_url": "/queryMY?queries=",
+                "update_url": "/ups_QMY?queries=",
+                "fortune_url": "/f_RW_QMY",
                 "port": 8080,
                 "port": 8080,
                 "approach": "Realistic",
                 "approach": "Realistic",
                 "classification": "Fullstack",
                 "classification": "Fullstack",
@@ -132,10 +133,11 @@
                 "versus": ""
                 "versus": ""
             },
             },
             "thread-apg": {
             "thread-apg": {
-                "db_url": "/dbp",
-                "query_url": "/queriesp?queries=",
+                "db_url": "/pg",
+                "query_url": "/querAPG?queries=",
                 "update_url": "/updatep?queries=",
                 "update_url": "/updatep?queries=",
-                "fortune_url": "/fortunes_raw_p",
+                "fortune_url": "/f_RW_APG",
+                "cached_query_url": "/cached_queries?count=",
                 "port": 8080,
                 "port": 8080,
                 "approach": "Realistic",
                 "approach": "Realistic",
                 "classification": "Fullstack",
                 "classification": "Fullstack",
@@ -169,10 +171,10 @@
                 "versus": ""
                 "versus": ""
             },
             },
             "thread-pg": {
             "thread-pg": {
-                "db_url": "/db_postgres",
-                "query_url": "/query_postgres?queries=",
-                "update_url": "/updates_postgres?queries=",
-                "fortune_url": "/fortunes_raw_postgres",
+                "db_url": "/PG",
+                "query_url": "/queryPG?queries=",
+                "update_url": "/ups_QPG?queries=",
+                "fortune_url": "/f_RW_QPG",
                 "port": 8080,
                 "port": 8080,
                 "approach": "Realistic",
                 "approach": "Realistic",
                 "classification": "Fullstack",
                 "classification": "Fullstack",
@@ -189,7 +191,7 @@
                 "versus": ""
                 "versus": ""
             },
             },
             "thread-pg-cutelee": {
             "thread-pg-cutelee": {
-                "fortune_url": "/fortunes_cutelee_postgres",
+                "fortune_url": "/f_CL_QPG",
                 "port": 8080,
                 "port": 8080,
                 "approach": "Realistic",
                 "approach": "Realistic",
                 "classification": "Fullstack",
                 "classification": "Fullstack",
@@ -206,7 +208,7 @@
                 "versus": ""
                 "versus": ""
             },
             },
             "t-apg-cutelee": {
             "t-apg-cutelee": {
-                "fortune_url": "/fortunes_c_p",
+                "fortune_url": "/f_CL_APG",
                 "port": 8080,
                 "port": 8080,
                 "approach": "Realistic",
                 "approach": "Realistic",
                 "classification": "Fullstack",
                 "classification": "Fullstack",
@@ -223,10 +225,10 @@
                 "versus": ""
                 "versus": ""
             },
             },
             "thread-my": {
             "thread-my": {
-                "db_url": "/db_mysql",
-                "query_url": "/query_mysql?queries=",
-                "update_url": "/updates_mysql?queries=",
-                "fortune_url": "/fortunes_raw_mysql",
+                "db_url": "/MY",
+                "query_url": "/queryMY?queries=",
+                "update_url": "/ups_QMY?queries=",
+                "fortune_url": "/f_RW_QMY",
                 "port": 8080,
                 "port": 8080,
                 "approach": "Realistic",
                 "approach": "Realistic",
                 "classification": "Fullstack",
                 "classification": "Fullstack",
@@ -243,7 +245,7 @@
                 "versus": ""
                 "versus": ""
             },
             },
             "thread-my-cutelee": {
             "thread-my-cutelee": {
-                "fortune_url": "/fortunes_cutelee_mysql",
+                "fortune_url": "/f_CL_QMY",
                 "port": 8080,
                 "port": 8080,
                 "approach": "Realistic",
                 "approach": "Realistic",
                 "classification": "Fullstack",
                 "classification": "Fullstack",

+ 14 - 13
frameworks/C++/cutelyst/build.sh

@@ -1,28 +1,29 @@
 #!/bin/bash
 #!/bin/bash
 
 
-export ASQL_VER=0.46.0
-export CUTELEE_VER=6.0.0
-export CUTELYST_VER=3.1.0
+export ASQL_VER=0.84.0
+export CUTELEE_VER=6.1.0
+export CUTELYST_TAG=v4.0.0-alpha1
+export CUTELYST_VER=4.0.0
 
 
 apt update -qq && \
 apt update -qq && \
     apt install -yqq --no-install-recommends \
     apt install -yqq --no-install-recommends \
     cmake \
     cmake \
     git \
     git \
     pkg-config \
     pkg-config \
-    qtbase5-dev \
-    libqt5sql5-mysql \
-    libqt5sql5-psql \
-    qtdeclarative5-dev \
+    qt6-base-dev \
+    libqt6sql6-mysql \
+    libqt6sql6-psql \
+    libegl1-mesa-dev \
     postgresql-server-dev-all
     postgresql-server-dev-all
 
 
-wget -q https://github.com/cutelyst/cutelee/releases/download/v${CUTELEE_VER}/cutelee_${CUTELEE_VER}_amd64.deb && \
-    apt install -yqq ./cutelee_${CUTELEE_VER}_amd64.deb
+wget -q https://github.com/cutelyst/cutelee/releases/download/v${CUTELEE_VER}/cutelee6-qt6_${CUTELEE_VER}_amd64.deb && \
+    apt install -yqq ./cutelee6-qt6_${CUTELEE_VER}_amd64.deb
 
 
-wget -q https://github.com/cutelyst/asql/releases/download/v${ASQL_VER}/libasql_${ASQL_VER}_amd64.deb && \
-    apt install -yqq ./libasql_${ASQL_VER}_amd64.deb
+wget -q https://github.com/cutelyst/asql/releases/download/v${ASQL_VER}/libasql0-qt6_${ASQL_VER}_amd64.deb && \
+    apt install -yqq ./libasql0-qt6_${ASQL_VER}_amd64.deb
 
 
-wget -q https://github.com/cutelyst/cutelyst/releases/download/v${CUTELYST_VER}/cutelyst_${CUTELYST_VER}_amd64.deb && \
-    apt install -yqq ./cutelyst_${CUTELYST_VER}_amd64.deb
+wget -q https://github.com/cutelyst/cutelyst/releases/download/${CUTELYST_TAG}/cutelyst4-qt6_${CUTELYST_VER}_amd64.deb && \
+    apt install -yqq ./cutelyst4-qt6_${CUTELYST_VER}_amd64.deb
 
 
 cd ${TROOT} && \
 cd ${TROOT} && \
     mkdir -p build && \
     mkdir -p build && \

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-nginx-my.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-nginx-pg.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-nginx.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-pf-apg-batch.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-pf-apg.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-pf-my.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-pf-pg.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-t-apg-cutelee.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-thread-apg-batch.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-thread-apg.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-thread-my-cutelee.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-thread-my.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-thread-pg-cutelee.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-thread-pg.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-thread-pico.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst-thread.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 1 - 1
frameworks/C++/cutelyst/cutelyst.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:20.04
+FROM ubuntu:22.04
 
 
 RUN apt-get update -qq && \
 RUN apt-get update -qq && \
     apt-get install -yqq locales wget build-essential
     apt-get install -yqq locales wget build-essential

+ 16 - 14
frameworks/C++/cutelyst/src/CMakeLists.txt

@@ -13,10 +13,10 @@ FetchContent_Declare(
 )
 )
 FetchContent_MakeAvailable(mimalloc)
 FetchContent_MakeAvailable(mimalloc)
 
 
-find_package(Qt5 5.6.0 REQUIRED COMPONENTS Core Network Sql)
-find_package(ASqlQt5 0.43.0 REQUIRED)
-find_package(Cutelyst3Qt5 3.1 REQUIRED)
-find_package(Cutelee6Qt5 6.0.0 REQUIRED)
+find_package(Qt6 6.2.0 REQUIRED COMPONENTS Core Network Sql)
+find_package(ASqlQt6 0.84 REQUIRED)
+find_package(Cutelyst4Qt6 3.6 REQUIRED)
+find_package(Cutelee6Qt6 6.1.0 REQUIRED)
 find_package(PostgreSQL REQUIRED)
 find_package(PostgreSQL REQUIRED)
 
 
 # Auto generate moc files
 # Auto generate moc files
@@ -44,10 +44,10 @@ set(cutelyst_benchmarks_SRCS
     multipledatabasequeriestest.h
     multipledatabasequeriestest.h
     plaintexttest.cpp
     plaintexttest.cpp
     plaintexttest.h
     plaintexttest.h
-    root.cpp
-    root.h
     singledatabasequerytest.cpp
     singledatabasequerytest.cpp
     singledatabasequerytest.h
     singledatabasequerytest.h
+    cachedqueries.h
+    cachedqueries.cpp
     ${TEMPLATES_SRC}
     ${TEMPLATES_SRC}
 )
 )
 
 
@@ -60,10 +60,11 @@ target_link_libraries(cutelyst_benchmarks
     Cutelyst::Core
     Cutelyst::Core
     Cutelyst::Utils::Sql
     Cutelyst::Utils::Sql
     Cutelyst::View::Cutelee
     Cutelyst::View::Cutelee
-    Qt5::Core
-    Qt5::Network
-    Qt5::Sql
-    ASqlQt5::Core
+    Qt::Core
+    Qt::Network
+    Qt::Sql
+    ASql::Core
+    ASql::Pg
 )
 )
 
 
 add_executable(cutelyst-benchmarks ${cutelyst_benchmarks_SRCS} main.cpp)
 add_executable(cutelyst-benchmarks ${cutelyst_benchmarks_SRCS} main.cpp)
@@ -73,10 +74,11 @@ target_link_libraries(cutelyst-benchmarks
     Cutelyst::Server
     Cutelyst::Server
     Cutelyst::Utils::Sql
     Cutelyst::Utils::Sql
     Cutelyst::View::Cutelee
     Cutelyst::View::Cutelee
-    Qt5::Core
-    Qt5::Network
-    Qt5::Sql
-    ASqlQt5::Core
+    Qt::Core
+    Qt::Network
+    Qt::Sql
+    ASql::Core
+    ASql::Pg
     mimalloc
     mimalloc
 )
 )
 if (mimalloc_FOUND)
 if (mimalloc_FOUND)

+ 70 - 0
frameworks/C++/cutelyst/src/cachedqueries.cpp

@@ -0,0 +1,70 @@
+#include "cachedqueries.h"
+
+#include <apool.h>
+#include <aresult.h>
+#include <apreparedquery.h>
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+
+#include <QCache>
+
+using namespace ASql;
+
+CachedQueries::CachedQueries(QObject *parent)
+    : Controller{parent}
+{
+
+}
+
+void CachedQueries::cached_queries(Context *c)
+{
+    int queries = c->request()->queryParam(u"count"_qs).toInt();
+    if (queries < 1) {
+        queries = 1;
+    } else if (queries > 500) {
+        queries = 500;
+    }
+
+    static thread_local QCache<int, QJsonObject> cache(1024);
+
+    auto array = std::shared_ptr<QJsonArray>(new QJsonArray);
+
+    ASync async(c);
+    static thread_local auto db = APool::database();
+    for (int i = 0; i < queries; ++i) {
+        const int id = (rand() % 10000) + 1;
+
+        QJsonObject *obj = cache[id];
+        if (obj) {
+            array->append(*obj);
+            continue;
+        }
+
+        db.exec(APreparedQueryLiteral(u8"SELECT id, randomNumber FROM world WHERE id=$1"),
+                               {id}, c, [c, async, i, queries, array] (AResult &result) {
+            if (Q_LIKELY(!result.error() && result.size())) {
+                auto it = result.begin();
+                int id = it[0].toInt();
+                auto obj = new QJsonObject({
+                                               {u"id"_qs, id},
+                                               {u"randomNumber"_qs, it[1].toInt()}
+                                           });
+                array->append(*obj);
+                cache.insert(id, obj, 1);
+
+                if (array->size() == queries) {
+                    c->response()->setJsonArrayBody(*array);
+                }
+                return;
+            }
+
+            c->res()->setStatus(Response::InternalServerError);
+        });
+    }
+
+    if (array->size() == queries) {
+        c->response()->setJsonArrayBody(*array);
+    }
+}

+ 19 - 0
frameworks/C++/cutelyst/src/cachedqueries.h

@@ -0,0 +1,19 @@
+#ifndef CACHEDQUERIES_H
+#define CACHEDQUERIES_H
+
+#include <Cutelyst/Controller>
+
+using namespace Cutelyst;
+
+class CachedQueries : public Controller
+{
+    Q_OBJECT
+    C_NAMESPACE("")
+public:
+    explicit CachedQueries(QObject *parent = nullptr);
+
+    C_ATTR(cached_queries, :Local :AutoArgs)
+    void cached_queries(Context *c);
+};
+
+#endif // CACHEDQUERIES_H

+ 31 - 27
frameworks/C++/cutelyst/src/cutelyst-benchmarks.cpp

@@ -13,6 +13,7 @@
 #include <QDir>
 #include <QDir>
 
 
 #include <apool.h>
 #include <apool.h>
+#include <apg.h>
 
 
 #include "jsontest.h"
 #include "jsontest.h"
 #include "singledatabasequerytest.h"
 #include "singledatabasequerytest.h"
@@ -20,14 +21,17 @@
 #include "databaseupdatestest.h"
 #include "databaseupdatestest.h"
 #include "fortunetest.h"
 #include "fortunetest.h"
 #include "plaintexttest.h"
 #include "plaintexttest.h"
+#include "cachedqueries.h"
 
 
 using namespace Cutelyst;
 using namespace Cutelyst;
+using namespace ASql;
 
 
 static QMutex mutex;
 static QMutex mutex;
 
 
 cutelyst_benchmarks::cutelyst_benchmarks(QObject *parent) : Application(parent)
 cutelyst_benchmarks::cutelyst_benchmarks(QObject *parent) : Application(parent)
 {
 {
-    qsrand(QDateTime::currentMSecsSinceEpoch());
+    static std::once_flag once;
+    std::call_once(once, []() { srand(time(NULL)); });
 }
 }
 
 
 cutelyst_benchmarks::~cutelyst_benchmarks()
 cutelyst_benchmarks::~cutelyst_benchmarks()
@@ -36,7 +40,7 @@ cutelyst_benchmarks::~cutelyst_benchmarks()
 
 
 bool cutelyst_benchmarks::init()
 bool cutelyst_benchmarks::init()
 {
 {
-    if (config(QStringLiteral("SendDate")).value<bool>()) {
+    if (config(u"SendDate"_qs).value<bool>()) {
         qDebug() << "Manually send date";
         qDebug() << "Manually send date";
         auto dateT = new QTimer(this);
         auto dateT = new QTimer(this);
         dateT->setInterval(1000);
         dateT->setInterval(1000);
@@ -56,11 +60,12 @@ bool cutelyst_benchmarks::init()
     new DatabaseUpdatesTest(this);
     new DatabaseUpdatesTest(this);
     new FortuneTest(this);
     new FortuneTest(this);
     new PlaintextTest(this);
     new PlaintextTest(this);
+    new CachedQueries(this);
 
 
     if (defaultHeaders().server().isEmpty()) {
     if (defaultHeaders().server().isEmpty()) {
-        defaultHeaders().setServer(QStringLiteral("Cutelyst"));
+        defaultHeaders().setServer("Cutelyst"_qba);
     }
     }
-    defaultHeaders().removeHeader(QStringLiteral("X-Cutelyst"));
+    defaultHeaders().removeHeader("X-Cutelyst");
 
 
     return true;
     return true;
 }
 }
@@ -70,42 +75,41 @@ bool cutelyst_benchmarks::postFork()
     QMutexLocker locker(&mutex); // QSqlDatabase::addDatabase is not thread-safe
     QMutexLocker locker(&mutex); // QSqlDatabase::addDatabase is not thread-safe
 
 
     QSqlDatabase db;
     QSqlDatabase db;
-    const auto driver = config(QStringLiteral("Driver")).toString();
-    if (driver == QLatin1String("QPSQL")) {
-        db = QSqlDatabase::addDatabase(driver, Sql::databaseNameThread(QStringLiteral("postgres")));
-        db.setDatabaseName(QStringLiteral("hello_world"));
-        db.setUserName(QStringLiteral("benchmarkdbuser"));
-        db.setPassword(QStringLiteral("benchmarkdbpass"));
-        db.setHostName(config(QStringLiteral("DatabaseHostName")).toString());
+    const auto driver = config(u"Driver"_qs).toString();
+    if (driver == u"QPSQL") {
+        db = QSqlDatabase::addDatabase(driver, Sql::databaseNameThread(u"postgres"_qs));
+        db.setDatabaseName(u"hello_world"_qs);
+        db.setUserName(u"benchmarkdbuser"_qs);
+        db.setPassword(u"benchmarkdbpass"_qs);
+        db.setHostName(config(u"DatabaseHostName"_qs).toString());
         if (!db.open()) {
         if (!db.open()) {
             qDebug() << "Error opening PostgreSQL db:" << db << db.connectionName() << db.lastError().databaseText();
             qDebug() << "Error opening PostgreSQL db:" << db << db.connectionName() << db.lastError().databaseText();
             return false;
             return false;
         }
         }
-    } else if (driver == QLatin1String("QMYSQL")) {
-        db = QSqlDatabase::addDatabase(driver, Sql::databaseNameThread(QStringLiteral("mysql")));
-        db.setDatabaseName(QStringLiteral("hello_world"));
-        db.setUserName(QStringLiteral("benchmarkdbuser"));
-        db.setPassword(QStringLiteral("benchmarkdbpass"));
-        db.setHostName(config(QStringLiteral("DatabaseHostName")).toString());
+    } else if (driver == u"QMYSQL") {
+        db = QSqlDatabase::addDatabase(driver, Sql::databaseNameThread(u"mysql"_qs));
+        db.setDatabaseName(u"hello_world"_qs);
+        db.setUserName(u"benchmarkdbuser"_qs);
+        db.setPassword(u"benchmarkdbpass"_qs);
+        db.setHostName(config(u"DatabaseHostName"_qs).toString());
         if (!db.open()) {
         if (!db.open()) {
             qDebug() << "Error opening MySQL db:" << db << db.connectionName() << db.lastError().databaseText();
             qDebug() << "Error opening MySQL db:" << db << db.connectionName() << db.lastError().databaseText();
             return false;
             return false;
         }
         }
-    } else if (driver == QLatin1String("postgres")) {
-        QUrl uri(QStringLiteral("postgresql://benchmarkdbuser:benchmarkdbpass@server/hello_world"));
-        uri.setHost(config(QStringLiteral("DatabaseHostName")).toString());
+    } else if (driver == u"postgres") {
+        QUrl uri(u"postgresql://benchmarkdbuser:benchmarkdbpass@server/hello_world"_qs);
+        uri.setHost(config(u"DatabaseHostName"_qs).toString());
         qDebug() << "ASql URI:" << uri.toString();
         qDebug() << "ASql URI:" << uri.toString();
 
 
-        APool::addDatabase(uri.toString());
-        APool::setDatabaseMaxIdleConnections(128);
+        APool::create(ASql::APg::factory(uri.toString()));
+        APool::setMaxIdleConnections(128);
+        APool::setSetupCallback([](ADatabase db) {
+            // Enable Pipeline mode
+            db.enterPipelineMode(500);
+        });
     }
     }
 
 
     qDebug() << "Connections" << QCoreApplication::applicationPid() << QThread::currentThread() << QSqlDatabase::connectionNames();
     qDebug() << "Connections" << QCoreApplication::applicationPid() << QThread::currentThread() << QSqlDatabase::connectionNames();
-//    db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("sqlite"));
-//    if (!db.open()) {
-//        qDebug() << "Error opening db:" << db << db.lastError().databaseText();
-//        return false;
-//    }
 
 
     return true;
     return true;
 }
 }

+ 55 - 54
frameworks/C++/cutelyst/src/databaseupdatestest.cpp

@@ -16,6 +16,8 @@
 
 
 #include "picojson.h"
 #include "picojson.h"
 
 
+using namespace ASql;
+
 DatabaseUpdatesTest::DatabaseUpdatesTest(QObject *parent) : Controller(parent)
 DatabaseUpdatesTest::DatabaseUpdatesTest(QObject *parent) : Controller(parent)
 {
 {
 
 
@@ -23,7 +25,7 @@ DatabaseUpdatesTest::DatabaseUpdatesTest(QObject *parent) : Controller(parent)
 
 
 void DatabaseUpdatesTest::updatep(Context *c)
 void DatabaseUpdatesTest::updatep(Context *c)
 {
 {
-    int queries = c->request()->queryParam(QStringLiteral("queries"), QStringLiteral("1")).toInt();
+    int queries = c->request()->queryParam(u"queries"_qs, u"1"_qs).toInt();
     if (queries < 1) {
     if (queries < 1) {
         queries = 1;
         queries = 1;
     } else if (queries > 500) {
     } else if (queries > 500) {
@@ -34,29 +36,29 @@ void DatabaseUpdatesTest::updatep(Context *c)
     ASync async(c);
     ASync async(c);
     static thread_local auto db = APool::database();
     static thread_local auto db = APool::database();
     for (int i = 0; i < queries; ++i) {
     for (int i = 0; i < queries; ++i) {
-        int id = (qrand() % 10000) + 1;
+        int id = (rand() % 10000) + 1;
 
 
-        int randomNumber = (qrand() % 10000) + 1;
+        int randomNumber = (rand() % 10000) + 1;
 
 
         array.emplace_back(picojson::object({
         array.emplace_back(picojson::object({
                             {"id", picojson::value(double(id))},
                             {"id", picojson::value(double(id))},
                             {"randomNumber", picojson::value(double(randomNumber))}
                             {"randomNumber", picojson::value(double(randomNumber))}
                         }));
                         }));
 
 
-        db.exec(APreparedQueryLiteral(u"SELECT randomNumber, id FROM world WHERE id=$1"),
-                               {id}, [c, async] (AResult &result) {
+        db.exec(APreparedQueryLiteral(u8"SELECT randomNumber, id FROM world WHERE id=$1"),
+                               {id}, c, [c, async] (AResult &result) {
             if (Q_UNLIKELY(result.error() || !result.size())) {
             if (Q_UNLIKELY(result.error() || !result.size())) {
                 c->res()->setStatus(Response::InternalServerError);
                 c->res()->setStatus(Response::InternalServerError);
                 return;
                 return;
             }
             }
-        }, c);
-        db.exec(APreparedQueryLiteral(u"UPDATE world SET randomNumber=$1 WHERE id=$2"),
-                               {randomNumber, id}, [c, async] (AResult &result) {
+        });
+        db.exec(APreparedQueryLiteral(u8"UPDATE world SET randomNumber=$1 WHERE id=$2"),
+                               {randomNumber, id}, c, [c, async] (AResult &result) {
             if (Q_UNLIKELY(result.error())) {
             if (Q_UNLIKELY(result.error())) {
                 c->res()->setStatus(Response::InternalServerError);
                 c->res()->setStatus(Response::InternalServerError);
                 return;
                 return;
             }
             }
-        }, c);
+        });
     }
     }
 
 
     c->response()->setJsonBody(QByteArray::fromStdString(picojson::value(array).serialize()));
     c->response()->setJsonBody(QByteArray::fromStdString(picojson::value(array).serialize()));
@@ -64,7 +66,7 @@ void DatabaseUpdatesTest::updatep(Context *c)
 
 
 void DatabaseUpdatesTest::updateb(Context *c)
 void DatabaseUpdatesTest::updateb(Context *c)
 {
 {
-    int queries = c->request()->queryParam(QStringLiteral("queries"), QStringLiteral("1")).toInt();
+    int queries = c->request()->queryParam(u"queries"_qs, u"1"_qs).toInt();
     if (queries < 1) {
     if (queries < 1) {
         queries = 1;
         queries = 1;
     } else if (queries > 500) {
     } else if (queries > 500) {
@@ -78,9 +80,9 @@ void DatabaseUpdatesTest::updateb(Context *c)
     ASync async(c);
     ASync async(c);
     static thread_local auto db = APool::database();
     static thread_local auto db = APool::database();
     for (int i = 0; i < queries; ++i) {
     for (int i = 0; i < queries; ++i) {
-        int id = (qrand() % 10000) + 1;
+        int id = (rand() % 10000) + 1;
 
 
-        int randomNumber = (qrand() % 10000) + 1;
+        int randomNumber = (rand() % 10000) + 1;
 
 
         argsIds.append(id);
         argsIds.append(id);
         args.append(id);
         args.append(id);
@@ -91,23 +93,23 @@ void DatabaseUpdatesTest::updateb(Context *c)
                             {"randomNumber", picojson::value(double(randomNumber))}
                             {"randomNumber", picojson::value(double(randomNumber))}
                         }));
                         }));
 
 
-        db.exec(APreparedQueryLiteral(u"SELECT randomNumber, id FROM world WHERE id=$1"),
-                               {id}, [c, async] (AResult &result) {
+        db.exec(APreparedQueryLiteral(u8"SELECT randomNumber, id FROM world WHERE id=$1"),
+                               {id}, c, [c, async] (AResult &result) {
             if (Q_UNLIKELY(result.error() || !result.size())) {
             if (Q_UNLIKELY(result.error() || !result.size())) {
                 c->res()->setStatus(Response::InternalServerError);
                 c->res()->setStatus(Response::InternalServerError);
                 return;
                 return;
             }
             }
-        }, c);
+        });
     }
     }
     args.append(argsIds);
     args.append(argsIds);
 
 
     const APreparedQuery pq = getSql(queries);
     const APreparedQuery pq = getSql(queries);
-    db.exec(pq, args, [c, async] (AResult &result) {
+    db.exec(pq, args, c, [c, async] (AResult &result) {
         if (Q_UNLIKELY(result.error())) {
         if (Q_UNLIKELY(result.error())) {
             c->res()->setStatus(Response::InternalServerError);
             c->res()->setStatus(Response::InternalServerError);
             return;
             return;
         }
         }
-    }, c);
+    });
 
 
     c->response()->setJsonBody(QByteArray::fromStdString(picojson::value(array).serialize()));
     c->response()->setJsonBody(QByteArray::fromStdString(picojson::value(array).serialize()));
 }
 }
@@ -115,22 +117,22 @@ void DatabaseUpdatesTest::updateb(Context *c)
 void DatabaseUpdatesTest::updates_postgres(Context *c)
 void DatabaseUpdatesTest::updates_postgres(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QStringLiteral("postgres"));
+                u"SELECT id, randomNumber FROM world WHERE id = :id"_qs,
+                u"postgres"_qs);
     QSqlQuery updateQuery = CPreparedSqlQueryThreadForDB(
     QSqlQuery updateQuery = CPreparedSqlQueryThreadForDB(
-                QLatin1String("UPDATE world SET randomNumber = :randomNumber WHERE id = :id"),
-                QStringLiteral("postgres"));
+                u"UPDATE world SET randomNumber = :randomNumber WHERE id = :id"_qs,
+                u"postgres"_qs);
     processQuery(c, query, updateQuery);
     processQuery(c, query, updateQuery);
 }
 }
 
 
 void DatabaseUpdatesTest::updates_mysql(Context *c)
 void DatabaseUpdatesTest::updates_mysql(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT randomNumber, id FROM world WHERE id = :id"),
-                QStringLiteral("mysql"));
+                u"SELECT randomNumber, id FROM world WHERE id = :id"_qs,
+                u"mysql"_qs);
     QSqlQuery updateQuery = CPreparedSqlQueryThreadForDB(
     QSqlQuery updateQuery = CPreparedSqlQueryThreadForDB(
-                QLatin1String("UPDATE world SET randomNumber = :randomNumber WHERE id = :id"),
-                QStringLiteral("mysql"));
+                u"UPDATE world SET randomNumber = :randomNumber WHERE id = :id"_qs,
+                u"mysql"_qs);
     processQuery(c, query, updateQuery);
     processQuery(c, query, updateQuery);
 }
 }
 
 
@@ -138,7 +140,7 @@ void DatabaseUpdatesTest::processQuery(Context *c, QSqlQuery &query, QSqlQuery &
 {
 {
     QJsonArray array;
     QJsonArray array;
 
 
-    int queries = c->request()->queryParam(QStringLiteral("queries"), QStringLiteral("1")).toInt();
+    int queries = c->request()->queryParam(u"queries"_qs, u"1"_qs).toInt();
     if (queries < 1) {
     if (queries < 1) {
         queries = 1;
         queries = 1;
     } else if (queries > 500) {
     } else if (queries > 500) {
@@ -149,26 +151,26 @@ void DatabaseUpdatesTest::processQuery(Context *c, QSqlQuery &query, QSqlQuery &
     ids.reserve(queries);
     ids.reserve(queries);
     randomNumbers.reserve(queries);
     randomNumbers.reserve(queries);
     for (int i = 0; i < queries; ++i) {
     for (int i = 0; i < queries; ++i) {
-        int id = (qrand() % 10000) + 1;
+        int id = (rand() % 10000) + 1;
 
 
-        query.bindValue(QStringLiteral(":id"), id);
+        query.bindValue(u":id"_qs, id);
         if (Q_UNLIKELY(!query.exec() || !query.next())) {
         if (Q_UNLIKELY(!query.exec() || !query.next())) {
             c->res()->setStatus(Response::InternalServerError);
             c->res()->setStatus(Response::InternalServerError);
             return;
             return;
         }
         }
 
 
-        int randomNumber = (qrand() % 10000) + 1;
+        int randomNumber = (rand() % 10000) + 1;
         ids.append(id);
         ids.append(id);
         randomNumbers.append(randomNumber);
         randomNumbers.append(randomNumber);
 
 
         array.append(QJsonObject{
         array.append(QJsonObject{
-                         {QStringLiteral("id"), id},
-                         {QStringLiteral("randomNumber"), randomNumber}
+                         {u"id"_qs, id},
+                         {u"randomNumber"_qs, randomNumber}
                      });
                      });
     }
     }
 
 
-    updateQuery.bindValue(QStringLiteral(":id"), ids);
-    updateQuery.bindValue(QStringLiteral(":randomNumber"), randomNumbers);
+    updateQuery.bindValue(u":id"_qs, ids);
+    updateQuery.bindValue(u":randomNumber"_qs, randomNumbers);
     if (Q_LIKELY(updateQuery.execBatch())) {
     if (Q_LIKELY(updateQuery.execBatch())) {
         c->response()->setJsonArrayBody(array);
         c->response()->setJsonArrayBody(array);
     } else {
     } else {
@@ -179,29 +181,28 @@ void DatabaseUpdatesTest::processQuery(Context *c, QSqlQuery &query, QSqlQuery &
 APreparedQuery DatabaseUpdatesTest::getSql(int count)
 APreparedQuery DatabaseUpdatesTest::getSql(int count)
 {
 {
     auto iter = m_sqlMap.find(count);
     auto iter = m_sqlMap.find(count);
-    if (iter != m_sqlMap.end())
-    {
-        return iter.value();
-    }
-    QString sql = QStringLiteral("UPDATE WORLD SET randomnumber=CASE id ");
-    sql.reserve(80 + count * 25);
-    int placeholdersCounter = 1;
-    for (int i = 0; i < count; i++) {
-        sql.append(QStringLiteral("WHEN $%1 THEN $%2 ").arg(placeholdersCounter).arg(placeholdersCounter + 1));
-        placeholdersCounter += 2;
-    }
-    sql.append(QStringLiteral("ELSE randomnumber END WHERE id IN ("));
+    if (Q_UNLIKELY(iter == m_sqlMap.end())) {
+        QString sql = u"UPDATE WORLD SET randomnumber=CASE id "_qs;
+        sql.reserve(80 + count * 25);
+        int placeholdersCounter = 1;
+        for (int i = 0; i < count; i++) {
+            sql.append(u"WHEN $%1 THEN $%2 "_qs.arg(placeholdersCounter).arg(placeholdersCounter + 1));
+            placeholdersCounter += 2;
+        }
+        sql.append(u"ELSE randomnumber END WHERE id IN (");
 
 
-    for (int i = 0; i < count; i++) {
-        sql.append(QLatin1Char('$') + QString::number(placeholdersCounter) + QLatin1Char(','));
-        ++placeholdersCounter;
-    }
+        for (int i = 0; i < count; i++) {
+            sql.append(u'$' + QString::number(placeholdersCounter) + u',');
+            ++placeholdersCounter;
+        }
+
+        if (count) {
+            sql.remove(sql.size() - 1, 1);
+        }
+        sql.append(u')');
 
 
-    if (count) {
-        sql.remove(sql.size() - 1, 1);
+        iter = m_sqlMap.insert(count, APreparedQuery(sql));
     }
     }
-    sql.append(QLatin1Char(')'));
-    m_sqlMap.insert(count, sql);
 
 
-    return sql;
+    return iter.value();
 }
 }

+ 4 - 4
frameworks/C++/cutelyst/src/databaseupdatestest.h

@@ -20,17 +20,17 @@ public:
     C_ATTR(updateb, :Local :AutoArgs)
     C_ATTR(updateb, :Local :AutoArgs)
     void updateb(Context *c);
     void updateb(Context *c);
 
 
-    C_ATTR(updates_postgres, :Local :AutoArgs)
+    C_ATTR(updates_postgres, :Path('ups_QPG') :AutoArgs)
     void updates_postgres(Context *c);
     void updates_postgres(Context *c);
 
 
-    C_ATTR(updates_mysql, :Local :AutoArgs)
+    C_ATTR(updates_mysql, :Path('ups_QMY') :AutoArgs)
     void updates_mysql(Context *c);
     void updates_mysql(Context *c);
 
 
 private:
 private:
     inline void processQuery(Context *c, QSqlQuery &query, QSqlQuery &updateQuery);
     inline void processQuery(Context *c, QSqlQuery &query, QSqlQuery &updateQuery);
-    inline APreparedQuery getSql(int count);
+    inline ASql::APreparedQuery getSql(int count);
 
 
-    QMap<int, APreparedQuery> m_sqlMap;
+    QMap<int, ASql::APreparedQuery> m_sqlMap;
 };
 };
 
 
 #endif // DATABASEUPDATESTEST_H
 #endif // DATABASEUPDATESTEST_H

+ 29 - 27
frameworks/C++/cutelyst/src/fortunetest.cpp

@@ -9,6 +9,8 @@
 
 
 #include <QSqlQuery>
 #include <QSqlQuery>
 
 
+using namespace ASql;
+
 FortuneTest::FortuneTest(QObject *parent) : Controller(parent)
 FortuneTest::FortuneTest(QObject *parent) : Controller(parent)
 {
 {
 
 
@@ -18,7 +20,7 @@ void FortuneTest::fortunes_raw_p(Context *c)
 {
 {
     ASync async(c);
     ASync async(c);
     static thread_local auto db = APool::database();
     static thread_local auto db = APool::database();
-    db.exec(APreparedQueryLiteral(u"SELECT id, message FROM fortune"), [c, async, this] (AResult &result) {
+    db.exec(APreparedQueryLiteral(u8"SELECT id, message FROM fortune"), c, [c, async, this] (AResult &result) {
         if (Q_UNLIKELY(result.error() && !result.size())) {
         if (Q_UNLIKELY(result.error() && !result.size())) {
             c->res()->setStatus(Response::InternalServerError);
             c->res()->setStatus(Response::InternalServerError);
             return;
             return;
@@ -31,21 +33,21 @@ void FortuneTest::fortunes_raw_p(Context *c)
             fortunes.emplace_back(Fortune{it[0].toInt(), it[1].toString()});
             fortunes.emplace_back(Fortune{it[0].toInt(), it[1].toString()});
             ++it;
             ++it;
         }
         }
-        fortunes.emplace_back(Fortune{0, QStringLiteral("Additional fortune added at request time.")});
+        fortunes.emplace_back(Fortune{0, u"Additional fortune added at request time."_qs});
 
 
         std::sort(fortunes.begin(), fortunes.end(), [] (const Fortune &a1, const Fortune &a2) {
         std::sort(fortunes.begin(), fortunes.end(), [] (const Fortune &a1, const Fortune &a2) {
             return a1.message < a2.message;
             return a1.message < a2.message;
         });
         });
 
 
         renderRaw(c, fortunes);
         renderRaw(c, fortunes);
-    }, c);
+    });
 }
 }
 
 
 void FortuneTest::fortunes_raw_postgres(Context *c)
 void FortuneTest::fortunes_raw_postgres(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT id, message FROM fortune"),
-                QStringLiteral("postgres"));
+                u"SELECT id, message FROM fortune"_qs,
+                u"postgres"_qs);
     auto fortunes = processQuery(c, query);
     auto fortunes = processQuery(c, query);
     renderRaw(c, fortunes);
     renderRaw(c, fortunes);
 }
 }
@@ -53,8 +55,8 @@ void FortuneTest::fortunes_raw_postgres(Context *c)
 void FortuneTest::fortunes_raw_mysql(Context *c)
 void FortuneTest::fortunes_raw_mysql(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT id, message FROM fortune"),
-                QStringLiteral("mysql"));
+                u"SELECT id, message FROM fortune"_qs,
+                u"mysql"_qs);
     auto fortunes = processQuery(c, query);
     auto fortunes = processQuery(c, query);
     renderRaw(c, fortunes);
     renderRaw(c, fortunes);
 }
 }
@@ -63,7 +65,7 @@ void FortuneTest::fortunes_c_p(Context *c)
 {
 {
     ASync async(c);
     ASync async(c);
     static thread_local auto db = APool::database();
     static thread_local auto db = APool::database();
-    db.exec(APreparedQueryLiteral(u"SELECT id, message FROM fortune"), [c, async] (AResult &result) {
+    db.exec(APreparedQueryLiteral(u8"SELECT id, message FROM fortune"), c, [c, async] (AResult &result) {
         if (Q_UNLIKELY(result.error() && !result.size())) {
         if (Q_UNLIKELY(result.error() && !result.size())) {
             c->res()->setStatus(Response::InternalServerError);
             c->res()->setStatus(Response::InternalServerError);
             return;
             return;
@@ -80,59 +82,59 @@ void FortuneTest::fortunes_c_p(Context *c)
         }
         }
 
 
         fortunes.append(QVariant::fromValue(QVariantList{
         fortunes.append(QVariant::fromValue(QVariantList{
-                            {0, QStringLiteral("Additional fortune added at request time.")},
+                            {0, u"Additional fortune added at request time."_qs},
                         }));
                         }));
         std::sort(fortunes.begin(), fortunes.end(), [] (const QVariant &a1, const QVariant &a2) {
         std::sort(fortunes.begin(), fortunes.end(), [] (const QVariant &a1, const QVariant &a2) {
             return a1.toList()[1].toString() < a2.toList()[1].toString();
             return a1.toList()[1].toString() < a2.toList()[1].toString();
         });
         });
 
 
-        c->setStash(QStringLiteral("template"), QStringLiteral("fortunes.html"));
-        c->setStash(QStringLiteral("fortunes"), fortunes);
+        c->setStash(u"template"_qs, u"fortunes.html"_qs);
+        c->setStash(u"fortunes"_qs, fortunes);
         static thread_local View *view = c->view();
         static thread_local View *view = c->view();
         view->execute(c);
         view->execute(c);
-        c->response()->setContentType(QStringLiteral("text/html; charset=UTF-8"));
-    }, c);
+        c->response()->setContentType("text/html; charset=UTF-8"_qba);
+    });
 }
 }
 
 
 void FortuneTest::fortunes_cutelee_postgres(Context *c)
 void FortuneTest::fortunes_cutelee_postgres(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT id, message FROM fortune"),
-                QStringLiteral("postgres"));
+                u"SELECT id, message FROM fortune"_qs,
+                u"postgres"_qs);
     if (query.exec()) {
     if (query.exec()) {
         QVariantList fortunes = Sql::queryToList(query);
         QVariantList fortunes = Sql::queryToList(query);
         fortunes.append(QVariant::fromValue(QVariantList{
         fortunes.append(QVariant::fromValue(QVariantList{
-                            {0, QStringLiteral("Additional fortune added at request time.")},
+                            {0, u"Additional fortune added at request time."_qs},
                         }));
                         }));
         std::sort(fortunes.begin(), fortunes.end(), [] (const QVariant &a1, const QVariant &a2) {
         std::sort(fortunes.begin(), fortunes.end(), [] (const QVariant &a1, const QVariant &a2) {
             return a1.toList()[1].toString() < a2.toList()[1].toString();
             return a1.toList()[1].toString() < a2.toList()[1].toString();
         });
         });
-        c->setStash(QStringLiteral("template"), QStringLiteral("fortunes.html"));
-        c->setStash(QStringLiteral("fortunes"), fortunes);
+        c->setStash(u"template"_qs, u"fortunes.html"_qs);
+        c->setStash(u"fortunes"_qs, fortunes);
         static thread_local View *view = c->view();
         static thread_local View *view = c->view();
         view->execute(c);
         view->execute(c);
-        c->response()->setContentType(QStringLiteral("text/html; charset=UTF-8"));
+        c->response()->setContentType("text/html; charset=UTF-8"_qba);
     }
     }
 }
 }
 
 
 void FortuneTest::fortunes_cutelee_mysql(Context *c)
 void FortuneTest::fortunes_cutelee_mysql(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT id, message FROM fortune"),
-                QStringLiteral("mysql"));
+                u"SELECT id, message FROM fortune"_qs,
+                u"mysql"_qs);
     if (query.exec()) {
     if (query.exec()) {
         QVariantList fortunes = Sql::queryToList(query);
         QVariantList fortunes = Sql::queryToList(query);
         fortunes.append(QVariant::fromValue(QVariantList{
         fortunes.append(QVariant::fromValue(QVariantList{
-                            {0, QStringLiteral("Additional fortune added at request time.")},
+                            {0, u"Additional fortune added at request time."_qs},
                         }));
                         }));
         std::sort(fortunes.begin(), fortunes.end(), [] (const QVariant &a1, const QVariant &a2) {
         std::sort(fortunes.begin(), fortunes.end(), [] (const QVariant &a1, const QVariant &a2) {
             return a1.toList()[1].toString() < a2.toList()[1].toString();
             return a1.toList()[1].toString() < a2.toList()[1].toString();
         });
         });
-        c->setStash(QStringLiteral("template"), QStringLiteral("fortunes.html"));
-        c->setStash(QStringLiteral("fortunes"), fortunes);
+        c->setStash(u"template"_qs, u"fortunes.html"_qs);
+        c->setStash(u"fortunes"_qs, fortunes);
         static thread_local View *view = c->view();
         static thread_local View *view = c->view();
         view->execute(c);
         view->execute(c);
-        c->response()->setContentType(QStringLiteral("text/html; charset=UTF-8"));
+        c->response()->setContentType("text/html; charset=UTF-8"_qba);
     }
     }
 }
 }
 
 
@@ -149,7 +151,7 @@ FortuneList FortuneTest::processQuery(Context *c, QSqlQuery &query)
     while (query.next()) {
     while (query.next()) {
         fortunes.push_back({query.value(0).toInt(), query.value(1).toString()});
         fortunes.push_back({query.value(0).toInt(), query.value(1).toString()});
     }
     }
-    fortunes.push_back({0, QStringLiteral("Additional fortune added at request time.")});
+    fortunes.push_back({0, u"Additional fortune added at request time."_qs});
 
 
     std::sort(fortunes.begin(), fortunes.end(), [] (const Fortune &a1, const Fortune &a2) {
     std::sort(fortunes.begin(), fortunes.end(), [] (const Fortune &a1, const Fortune &a2) {
         return a1.message < a2.message;
         return a1.message < a2.message;
@@ -181,5 +183,5 @@ void FortuneTest::renderRaw(Context *c, const FortuneList &fortunes) const
 
 
     auto response = c->response();
     auto response = c->response();
     response->setBody(out);
     response->setBody(out);
-    response->setContentType(QStringLiteral("text/html; charset=UTF-8"));
+    response->setContentType("text/html; charset=UTF-8"_qba);
 }
 }

+ 6 - 6
frameworks/C++/cutelyst/src/fortunetest.h

@@ -19,22 +19,22 @@ class FortuneTest : public Controller
 public:
 public:
     explicit FortuneTest(QObject *parent = 0);
     explicit FortuneTest(QObject *parent = 0);
 
 
-    C_ATTR(fortunes_raw_p, :Local :AutoArgs)
+    C_ATTR(fortunes_raw_p, :Path('f_RW_APG') :AutoArgs)
     void fortunes_raw_p(Context *c);
     void fortunes_raw_p(Context *c);
 
 
-    C_ATTR(fortunes_raw_postgres, :Local :AutoArgs)
+    C_ATTR(fortunes_raw_postgres, :Path('f_RW_QPG') :AutoArgs)
     void fortunes_raw_postgres(Context *c);
     void fortunes_raw_postgres(Context *c);
 
 
-    C_ATTR(fortunes_raw_mysql, :Local :AutoArgs)
+    C_ATTR(fortunes_raw_mysql, :Path('f_RW_QMY') :AutoArgs)
     void fortunes_raw_mysql(Context *c);
     void fortunes_raw_mysql(Context *c);
 
 
-    C_ATTR(fortunes_c_p, :Local :AutoArgs)
+    C_ATTR(fortunes_c_p, :Path('f_CL_APG') :AutoArgs)
     void fortunes_c_p(Context *c);
     void fortunes_c_p(Context *c);
 
 
-    C_ATTR(fortunes_cutelee_postgres, :Local :AutoArgs)
+    C_ATTR(fortunes_cutelee_postgres, :Path('f_CL_QPG') :AutoArgs)
     void fortunes_cutelee_postgres(Context *c);
     void fortunes_cutelee_postgres(Context *c);
 
 
-    C_ATTR(fortunes_cutelee_mysql, :Local :AutoArgs)
+    C_ATTR(fortunes_cutelee_mysql, :Path('f_CL_QMY') :AutoArgs)
     void fortunes_cutelee_mysql(Context *c);
     void fortunes_cutelee_mysql(Context *c);
 
 
 private:
 private:

+ 1 - 1
frameworks/C++/cutelyst/src/jsontest.cpp

@@ -12,7 +12,7 @@ JsonTest::JsonTest(QObject *parent) : Controller(parent)
 
 
 void JsonTest::json(Context *c)
 void JsonTest::json(Context *c)
 {
 {
-    c->response()->setJsonObjectBody({ {QStringLiteral("message"), QStringLiteral("Hello, World!")} });
+    c->response()->setJsonObjectBody({ {u"message"_qs, u"Hello, World!"_qs} });
 }
 }
 
 
 void JsonTest::pson(Context *c)
 void JsonTest::pson(Context *c)

+ 18 - 16
frameworks/C++/cutelyst/src/multipledatabasequeriestest.cpp

@@ -12,6 +12,8 @@
 #include <QJsonObject>
 #include <QJsonObject>
 #include <QJsonArray>
 #include <QJsonArray>
 
 
+using namespace ASql;
+
 MultipleDatabaseQueriesTest::MultipleDatabaseQueriesTest(QObject *parent) : Controller(parent)
 MultipleDatabaseQueriesTest::MultipleDatabaseQueriesTest(QObject *parent) : Controller(parent)
 {
 {
 
 
@@ -19,7 +21,7 @@ MultipleDatabaseQueriesTest::MultipleDatabaseQueriesTest(QObject *parent) : Cont
 
 
 void MultipleDatabaseQueriesTest::queriesp(Context *c)
 void MultipleDatabaseQueriesTest::queriesp(Context *c)
 {
 {
-    int queries = c->request()->queryParam(QStringLiteral("queries")).toInt();
+    int queries = c->request()->queryParam(u"queries"_qs).toInt();
     if (queries < 1) {
     if (queries < 1) {
         queries = 1;
         queries = 1;
     } else if (queries > 500) {
     } else if (queries > 500) {
@@ -30,15 +32,15 @@ void MultipleDatabaseQueriesTest::queriesp(Context *c)
     ASync async(c);
     ASync async(c);
     static thread_local auto db = APool::database();
     static thread_local auto db = APool::database();
     for (int i = 0; i < queries; ++i) {
     for (int i = 0; i < queries; ++i) {
-        const int id = (qrand() % 10000) + 1;
+        const int id = (rand() % 10000) + 1;
 
 
-        db.exec(APreparedQueryLiteral(u"SELECT id, randomNumber FROM world WHERE id=$1"),
-                               {id}, [c, async, i, queries, array] (AResult &result) {
+        db.exec(APreparedQueryLiteral(u8"SELECT id, randomNumber FROM world WHERE id=$1"),
+                               {id}, c, [c, async, i, queries, array] (AResult &result) {
             if (Q_LIKELY(!result.error() && result.size())) {
             if (Q_LIKELY(!result.error() && result.size())) {
                 auto it = result.begin();
                 auto it = result.begin();
                 array->append(QJsonObject{
                 array->append(QJsonObject{
-                                  {QStringLiteral("id"), it[0].toInt()},
-                                  {QStringLiteral("randomNumber"), it[1].toInt()}
+                                  {u"id"_qs, it[0].toInt()},
+                                  {u"randomNumber"_qs, it[1].toInt()}
                               });
                               });
 
 
                 if (i + 1 == queries) {
                 if (i + 1 == queries) {
@@ -48,23 +50,23 @@ void MultipleDatabaseQueriesTest::queriesp(Context *c)
             }
             }
 
 
             c->res()->setStatus(Response::InternalServerError);
             c->res()->setStatus(Response::InternalServerError);
-        }, c);
+        });
     }
     }
 }
 }
 
 
 void MultipleDatabaseQueriesTest::query_postgres(Context *c)
 void MultipleDatabaseQueriesTest::query_postgres(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QStringLiteral("postgres"));
+                u"SELECT id, randomNumber FROM world WHERE id = :id"_qs,
+                u"postgres"_qs);
     processQuery(c, query);
     processQuery(c, query);
 }
 }
 
 
 void MultipleDatabaseQueriesTest::query_mysql(Context *c)
 void MultipleDatabaseQueriesTest::query_mysql(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QStringLiteral("mysql"));
+                u"SELECT id, randomNumber FROM world WHERE id = :id"_qs,
+                u"mysql"_qs);
     processQuery(c, query);
     processQuery(c, query);
 }
 }
 
 
@@ -72,7 +74,7 @@ void MultipleDatabaseQueriesTest::processQuery(Context *c, QSqlQuery &query)
 {
 {
     QJsonArray array;
     QJsonArray array;
 
 
-    int queries = c->request()->queryParam(QStringLiteral("queries")).toInt();
+    int queries = c->request()->queryParam(u"queries"_qs).toInt();
     if (queries < 1) {
     if (queries < 1) {
         queries = 1;
         queries = 1;
     } else if (queries > 500) {
     } else if (queries > 500) {
@@ -80,13 +82,13 @@ void MultipleDatabaseQueriesTest::processQuery(Context *c, QSqlQuery &query)
     }
     }
 
 
     for (int i = 0; i < queries; ++i) {
     for (int i = 0; i < queries; ++i) {
-        const int id = (qrand() % 10000) + 1;
+        const int id = (rand() % 10000) + 1;
 
 
-        query.bindValue(QStringLiteral(":id"), id);
+        query.bindValue(u":id"_qs, id);
         if (Q_LIKELY(query.exec() && query.next())) {
         if (Q_LIKELY(query.exec() && query.next())) {
             array.append(QJsonObject{
             array.append(QJsonObject{
-                             {QStringLiteral("id"), query.value(0).toInt()},
-                             {QStringLiteral("randomNumber"), query.value(1).toInt()}
+                             {u"id"_qs, query.value(0).toInt()},
+                             {u"randomNumber"_qs, query.value(1).toInt()}
                          });
                          });
         } else {
         } else {
             c->res()->setStatus(Response::InternalServerError);
             c->res()->setStatus(Response::InternalServerError);

+ 3 - 3
frameworks/C++/cutelyst/src/multipledatabasequeriestest.h

@@ -13,13 +13,13 @@ class MultipleDatabaseQueriesTest : public Controller
 public:
 public:
     explicit MultipleDatabaseQueriesTest(QObject *parent = 0);
     explicit MultipleDatabaseQueriesTest(QObject *parent = 0);
 
 
-    C_ATTR(queriesp, :Local :AutoArgs)
+    C_ATTR(queriesp, :Path('querAPG') :AutoArgs)
     void queriesp(Context *c);
     void queriesp(Context *c);
 
 
-    C_ATTR(query_postgres, :Local :AutoArgs)
+    C_ATTR(query_postgres, :Path('queryPG') :AutoArgs)
     void query_postgres(Context *c);
     void query_postgres(Context *c);
 
 
-    C_ATTR(query_mysql, :Local :AutoArgs)
+    C_ATTR(query_mysql, :Path('queryMY') :AutoArgs)
     void query_mysql(Context *c);
     void query_mysql(Context *c);
 
 
 private:
 private:

+ 2 - 2
frameworks/C++/cutelyst/src/plaintexttest.cpp

@@ -8,6 +8,6 @@ PlaintextTest::PlaintextTest(QObject *parent) : Controller(parent)
 void PlaintextTest::plaintext(Context *c)
 void PlaintextTest::plaintext(Context *c)
 {
 {
     Response *res = c->response();
     Response *res = c->response();
-    res->setBody(QByteArrayLiteral("Hello, World!"));
-    res->setContentType(QStringLiteral("text/plain"));
+    res->setBody("Hello, World!"_qba);
+    res->setContentType("text/plain"_qba);
 }
 }

+ 0 - 37
frameworks/C++/cutelyst/src/root.cpp

@@ -1,37 +0,0 @@
-#include "root.h"
-
-#include <QElapsedTimer>
-
-using namespace Cutelyst;
-
-Root::Root(QObject *parent) : Controller(parent)
-{
-}
-
-Root::~Root()
-{
-}
-
-QElapsedTimer timerSetup(Context *c)
-{
-    QElapsedTimer timer;
-    timer.start();
-    return timer;
-}
-
-QString setupHeader(Context *c)
-{
-    return c->response()->headers().setDateWithDateTime(QDateTime::currentDateTimeUtc());
-}
-
-void Root::End(Context *c)
-{
-    static thread_local QString lastDate = setupHeader(c);
-    static thread_local QElapsedTimer timer = timerSetup(c);
-    if (timer.hasExpired(1000)) {
-        lastDate = setupHeader(c);
-        timer.restart();
-    } else {
-        c->response()->setHeader(QStringLiteral("date"), lastDate);
-    }
-}

+ 0 - 22
frameworks/C++/cutelyst/src/root.h

@@ -1,22 +0,0 @@
-#ifndef ROOT_H
-#define ROOT_H
-
-#include <Cutelyst/Controller>
-
-using namespace Cutelyst;
-
-class Root : public Controller
-{
-    Q_OBJECT
-    C_NAMESPACE("")
-public:
-    explicit Root(QObject *parent = 0);
-    ~Root();
-
-private:
-    C_ATTR(End, :AutoArgs)
-    void End(Context *c);
-};
-
-#endif //ROOT_H
-

+ 46 - 13
frameworks/C++/cutelyst/src/singledatabasequerytest.cpp

@@ -10,22 +10,55 @@
 
 
 #include <QJsonDocument>
 #include <QJsonDocument>
 #include <QJsonObject>
 #include <QJsonObject>
+#include <QTimer>
 
 
 #include "picojson.h"
 #include "picojson.h"
 
 
+using namespace ASql;
+
 SingleDatabaseQueryTest::SingleDatabaseQueryTest(QObject *parent) : Controller(parent)
 SingleDatabaseQueryTest::SingleDatabaseQueryTest(QObject *parent) : Controller(parent)
 {
 {
 
 
 }
 }
 
 
-void SingleDatabaseQueryTest::dbp(Context *c)
+void SingleDatabaseQueryTest::db_asql_pg(Context *c)
 {
 {
-    const int id = (qrand() % 10000) + 1;
+    const int id = (rand() % 10000) + 1;
 
 
     ASync async(c);
     ASync async(c);
     static thread_local auto db = APool::database();
     static thread_local auto db = APool::database();
-    db.exec(APreparedQueryLiteral(u"SELECT id, randomNumber FROM world WHERE id=$1"),
-                           {id}, [c, async] (AResult &result) {
+
+    db.exec(APreparedQueryLiteral(u8"SELECT id, randomNumber FROM world WHERE id=$1"),
+                           {id}, c, [c, async] (AResult &result) {
+        if (Q_LIKELY(!result.error() && result.size())) {
+            auto it = result.begin();
+            c->response()->setJsonBody(QByteArray::fromStdString(
+                            picojson::value(picojson::object({
+                                                {"id", picojson::value(double(it[0].toInt()))},
+                                                {"randomNumber", picojson::value(double(it[1].toInt()))}
+                                            })).serialize()));
+            return;
+        }
+
+        c->res()->setStatus(Response::InternalServerError);
+    });
+}
+
+ADatabase getPipelineEnabledDatabase()
+{
+    auto db = APool::database();
+    db.enterPipelineMode(300);
+    return db;
+}
+
+void SingleDatabaseQueryTest::db_asql_pipeline_pg(Context *c)
+{
+    const int id = (rand() % 10000) + 1;
+
+    ASync async(c);
+    static thread_local auto db = getPipelineEnabledDatabase();
+    db.exec(APreparedQueryLiteral(u8"SELECT id, randomNumber FROM world WHERE id=$1"),
+                           {id}, c, [c, async] (AResult &result) {
         if (Q_LIKELY(!result.error() && result.size())) {
         if (Q_LIKELY(!result.error() && result.size())) {
             auto it = result.begin();
             auto it = result.begin();
             c->response()->setJsonBody(QByteArray::fromStdString(
             c->response()->setJsonBody(QByteArray::fromStdString(
@@ -37,37 +70,37 @@ void SingleDatabaseQueryTest::dbp(Context *c)
         }
         }
 
 
         c->res()->setStatus(Response::InternalServerError);
         c->res()->setStatus(Response::InternalServerError);
-    }, c);
+    });
 }
 }
 
 
 void SingleDatabaseQueryTest::db_postgres(Context *c)
 void SingleDatabaseQueryTest::db_postgres(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QStringLiteral("postgres"));
+                u"SELECT id, randomNumber FROM world WHERE id = :id"_qs,
+                u"postgres"_qs);
     processQuery(c, query);
     processQuery(c, query);
 }
 }
 
 
 void SingleDatabaseQueryTest::db_mysql(Context *c)
 void SingleDatabaseQueryTest::db_mysql(Context *c)
 {
 {
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
     QSqlQuery query = CPreparedSqlQueryThreadForDB(
-                QLatin1String("SELECT id, randomNumber FROM world WHERE id = :id"),
-                QStringLiteral("mysql"));
+                u"SELECT id, randomNumber FROM world WHERE id = :id"_qs,
+                u"mysql"_qs);
     processQuery(c, query);
     processQuery(c, query);
 }
 }
 
 
 void SingleDatabaseQueryTest::processQuery(Context *c, QSqlQuery &query)
 void SingleDatabaseQueryTest::processQuery(Context *c, QSqlQuery &query)
 {
 {
-    int id = (qrand() % 10000) + 1;
+    int id = (rand() % 10000) + 1;
 
 
-    query.bindValue(QStringLiteral(":id"), id);
+    query.bindValue(u":id"_qs, id);
     if (Q_UNLIKELY(!query.exec() || !query.next())) {
     if (Q_UNLIKELY(!query.exec() || !query.next())) {
         c->res()->setStatus(Response::InternalServerError);
         c->res()->setStatus(Response::InternalServerError);
         return;
         return;
     }
     }
 
 
     c->response()->setJsonObjectBody({
     c->response()->setJsonObjectBody({
-                                         {QStringLiteral("id"), query.value(0).toInt()},
-                                         {QStringLiteral("randomNumber"), query.value(1).toInt()}
+                                         {u"id"_qs, query.value(0).toInt()},
+                                         {u"randomNumber"_qs, query.value(1).toInt()}
                                      });
                                      });
 }
 }

+ 7 - 4
frameworks/C++/cutelyst/src/singledatabasequerytest.h

@@ -13,13 +13,16 @@ class SingleDatabaseQueryTest : public Controller
 public:
 public:
     explicit SingleDatabaseQueryTest(QObject *parent = 0);
     explicit SingleDatabaseQueryTest(QObject *parent = 0);
 
 
-    C_ATTR(dbp, :Local :AutoArgs)
-    void dbp(Context *c);
+    C_ATTR(db_asql_pg, :Path('pg') :AutoArgs)
+    void db_asql_pg(Context *c);
 
 
-    C_ATTR(db_postgres, :Local :AutoArgs)
+    C_ATTR(db_asql_pipeline_pg, :Path('Pg') :AutoArgs)
+    void db_asql_pipeline_pg(Context *c);
+
+    C_ATTR(db_postgres, :Path('PG') :AutoArgs)
     void db_postgres(Context *c);
     void db_postgres(Context *c);
 
 
-    C_ATTR(db_mysql, :Local :AutoArgs)
+    C_ATTR(db_mysql, :Path('MY') :AutoArgs)
     void db_mysql(Context *c);
     void db_mysql(Context *c);
 
 
 private:
 private:

+ 14 - 18
frameworks/C++/drogon/drogon-core.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:18.04
+FROM ubuntu:20.04
 
 
 COPY ./ ./
 COPY ./ ./
 
 
@@ -11,7 +11,7 @@ RUN  apt-get update -yqq && \
      zlib1g-dev && \
      zlib1g-dev && \
      add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
      add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
 	 apt-get update -yqq && \
 	 apt-get update -yqq && \
-	 apt-get install -yqq gcc-8 g++-8
+	 apt-get install -yqq gcc-10 g++-10
 
 
 RUN locale-gen en_US.UTF-8
 RUN locale-gen en_US.UTF-8
 
 
@@ -19,10 +19,10 @@ ENV LANG en_US.UTF-8
 ENV LANGUAGE en_US:en
 ENV LANGUAGE en_US:en
 ENV LC_ALL en_US.UTF-8
 ENV LC_ALL en_US.UTF-8
 
 
-ENV CC=gcc-8
-ENV CXX=g++-8
-ENV AR=gcc-ar-8
-ENV RANLIB=gcc-ranlib-8
+ENV CC=gcc-10
+ENV CXX=g++-10
+ENV AR=gcc-ar-10
+ENV RANLIB=gcc-ranlib-10
 
 
 ENV IROOT=/install
 ENV IROOT=/install
 ENV DROGON_ROOT=$IROOT/drogon
 ENV DROGON_ROOT=$IROOT/drogon
@@ -32,26 +32,22 @@ ENV TEST_PATH=/drogon_benchmark/build
 
 
 WORKDIR $IROOT
 WORKDIR $IROOT
 
 
-RUN wget https://github.com/an-tao/postgres/archive/batch_mode_ubuntu.tar.gz
-RUN tar -xvzf batch_mode_ubuntu.tar.gz
-WORKDIR $PG_ROOT
-
-RUN ./configure --prefix=/usr CFLAGS='-O2 -pipe'
-RUN make && make install
-
-WORKDIR $IROOT
+RUN sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
+RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
+RUN sudo apt -y update
+RUN sudo apt -y install postgresql-server-dev-all
 
 
 RUN git clone https://github.com/an-tao/drogon
 RUN git clone https://github.com/an-tao/drogon
 
 
 WORKDIR $DROGON_ROOT
 WORKDIR $DROGON_ROOT
 
 
-RUN git checkout fb17efe765d162be01680b05a3a387c7a182a4c5
+RUN git checkout ebf87d69d7bb45dfa478ba364ef9374d9be25092
 RUN git submodule update --init
 RUN git submodule update --init
 RUN mkdir build
 RUN mkdir build
 
 
 WORKDIR $DROGON_ROOT/build
 WORKDIR $DROGON_ROOT/build
 
 
-RUN cmake -DCMAKE_BUILD_TYPE=release ..
+RUN cmake -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_FLAGS=-flto ..
 RUN make && make install
 RUN make && make install
 
 
 WORKDIR $IROOT
 WORKDIR $IROOT
@@ -62,12 +58,12 @@ WORKDIR $MIMALLOC_ROOT
 RUN git checkout v1.6.7 -b v1.6.7
 RUN git checkout v1.6.7 -b v1.6.7
 RUN mkdir -p out/release
 RUN mkdir -p out/release
 WORKDIR $MIMALLOC_ROOT/out/release
 WORKDIR $MIMALLOC_ROOT/out/release
-RUN cmake ../..
+RUN cmake -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_FLAGS=-flto ../..
 RUN make && make install
 RUN make && make install
 
 
 WORKDIR $TEST_PATH
 WORKDIR $TEST_PATH
 
 
-RUN cmake -DCMAKE_BUILD_TYPE=release ..
+RUN cmake -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_FLAGS=-flto ..
 RUN make
 RUN make
 
 
 EXPOSE 8080
 EXPOSE 8080

+ 14 - 18
frameworks/C++/drogon/drogon.dockerfile

@@ -1,4 +1,4 @@
-FROM ubuntu:18.04
+FROM ubuntu:20.04
 
 
 COPY ./ ./
 COPY ./ ./
 
 
@@ -11,7 +11,7 @@ RUN  apt-get update -yqq && \
      zlib1g-dev && \
      zlib1g-dev && \
      add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
      add-apt-repository ppa:ubuntu-toolchain-r/test -y && \
 	 apt-get update -yqq && \
 	 apt-get update -yqq && \
-	 apt-get install -yqq gcc-8 g++-8
+	 apt-get install -yqq gcc-10 g++-10
 
 
 RUN locale-gen en_US.UTF-8
 RUN locale-gen en_US.UTF-8
 
 
@@ -19,10 +19,10 @@ ENV LANG en_US.UTF-8
 ENV LANGUAGE en_US:en
 ENV LANGUAGE en_US:en
 ENV LC_ALL en_US.UTF-8
 ENV LC_ALL en_US.UTF-8
 
 
-ENV CC=gcc-8
-ENV CXX=g++-8
-ENV AR=gcc-ar-8
-ENV RANLIB=gcc-ranlib-8
+ENV CC=gcc-10
+ENV CXX=g++-10
+ENV AR=gcc-ar-10
+ENV RANLIB=gcc-ranlib-10
 
 
 ENV IROOT=/install
 ENV IROOT=/install
 ENV DROGON_ROOT=$IROOT/drogon
 ENV DROGON_ROOT=$IROOT/drogon
@@ -32,26 +32,22 @@ ENV TEST_PATH=/drogon_benchmark/build
 
 
 WORKDIR $IROOT
 WORKDIR $IROOT
 
 
-RUN wget https://github.com/an-tao/postgres/archive/batch_mode_ubuntu.tar.gz
-RUN tar -xvzf batch_mode_ubuntu.tar.gz
-WORKDIR $PG_ROOT
-
-RUN ./configure --prefix=/usr CFLAGS='-O2 -pipe'
-RUN make && make install
-
-WORKDIR $IROOT
+RUN sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
+RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
+RUN sudo apt -y update
+RUN sudo apt -y install postgresql-server-dev-all
 
 
 RUN git clone https://github.com/an-tao/drogon
 RUN git clone https://github.com/an-tao/drogon
 
 
 WORKDIR $DROGON_ROOT
 WORKDIR $DROGON_ROOT
 
 
-RUN git checkout fb17efe765d162be01680b05a3a387c7a182a4c5
+RUN git checkout ebf87d69d7bb45dfa478ba364ef9374d9be25092
 RUN git submodule update --init
 RUN git submodule update --init
 RUN mkdir build
 RUN mkdir build
 
 
 WORKDIR $DROGON_ROOT/build
 WORKDIR $DROGON_ROOT/build
 
 
-RUN cmake -DCMAKE_BUILD_TYPE=release ..
+RUN cmake -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_FLAGS=-flto ..
 RUN make && make install
 RUN make && make install
 
 
 WORKDIR $IROOT
 WORKDIR $IROOT
@@ -62,12 +58,12 @@ WORKDIR $MIMALLOC_ROOT
 RUN git checkout v1.6.7 -b v1.6.7
 RUN git checkout v1.6.7 -b v1.6.7
 RUN mkdir -p out/release
 RUN mkdir -p out/release
 WORKDIR $MIMALLOC_ROOT/out/release
 WORKDIR $MIMALLOC_ROOT/out/release
-RUN cmake ../..
+RUN cmake -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_FLAGS=-flto ../..
 RUN make && make install
 RUN make && make install
 
 
 WORKDIR $TEST_PATH
 WORKDIR $TEST_PATH
 
 
-RUN cmake -DCMAKE_BUILD_TYPE=release ..
+RUN cmake -DCMAKE_BUILD_TYPE=release -DCMAKE_CXX_FLAGS=-flto ..
 RUN make
 RUN make
 
 
 EXPOSE 8080
 EXPOSE 8080

+ 2 - 1
frameworks/C++/drogon/drogon_benchmark/config-core.json

@@ -34,7 +34,8 @@
         //any synchronous interface of it.
         //any synchronous interface of it.
         "is_fast": true,
         "is_fast": true,
         //connection_number:1 by default
         //connection_number:1 by default
-        "connection_number": 1
+        "connection_number": 1,
+        "auto_batch": false
     }],
     }],
     "app": {
     "app": {
         //threads_num:the number of IO threads,1 by default, if the value is set to 0, the number of threads
         //threads_num:the number of IO threads,1 by default, if the value is set to 0, the number of threads

+ 3 - 2
frameworks/C++/drogon/drogon_benchmark/config.json

@@ -34,7 +34,8 @@
         //any synchronous interface of it.
         //any synchronous interface of it.
         "is_fast": true,
         "is_fast": true,
         //connection_number:1 by default
         //connection_number:1 by default
-        "connection_number": 1
+        "connection_number": 1,
+        "auto_batch": false
     }],
     }],
     "app": {
     "app": {
         //threads_num:the number of IO threads,1 by default, if the value is set to 0, the number of threads
         //threads_num:the number of IO threads,1 by default, if the value is set to 0, the number of threads
@@ -185,4 +186,4 @@
     }],
     }],
     //custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. 
     //custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method. 
     "custom_config": {}
     "custom_config": {}
-}
+}

+ 1 - 2
frameworks/C++/drogon/drogon_benchmark/plugins/SyncPlugin.cc

@@ -34,8 +34,7 @@ void SyncPlugin::initAndStart(const Json::Value &config)
                     {
                     {
                         auto resp = HttpResponse::newHttpResponse();
                         auto resp = HttpResponse::newHttpResponse();
                         resp->setBody("Hello, World!");
                         resp->setBody("Hello, World!");
-                        resp->setContentTypeCodeAndCustomString(
-                            CT_TEXT_PLAIN, "Content-Type: text/plain\r\n");
+                        resp->setContentTypeCode(CT_TEXT_PLAIN);
                         return resp;
                         return resp;
                     }
                     }
                     break;
                     break;

+ 7 - 7
frameworks/C++/ffead-cpp/benchmark_config.json

@@ -23,7 +23,7 @@
 			"display_name": "ffead-cpp-mongo",
 			"display_name": "ffead-cpp-mongo",
 			"notes": "mongodb redis",
 			"notes": "mongodb redis",
 			"versus": "",
 			"versus": "",
-			"tags": []
+			"tags": ["broken"]
 		},
 		},
 		"v-picov": {
 		"v-picov": {
 			"json_url": "/t3/j",
 			"json_url": "/t3/j",
@@ -95,7 +95,7 @@
 			"display_name": "ffead-cpp [v-prof-b]",
 			"display_name": "ffead-cpp [v-prof-b]",
 			"notes": "",
 			"notes": "",
 			"versus": "",
 			"versus": "",
-			"tags": []
+			"tags": ["broken"]
 		},
 		},
 		"postgresql-raw-profiled": {
 		"postgresql-raw-profiled": {
 			"db_url": "/t3/d",
 			"db_url": "/t3/d",
@@ -137,7 +137,7 @@
 			"display_name": "ffead-cpp [pg-raw-prof-b]",
 			"display_name": "ffead-cpp [pg-raw-prof-b]",
 			"notes": "memory libpq batch patch profiled",
 			"notes": "memory libpq batch patch profiled",
 			"versus": "",
 			"versus": "",
-			"tags": []
+			"tags": ["broken"]
 		},
 		},
 		"postgresql-raw-async-profiled": {
 		"postgresql-raw-async-profiled": {
 			"db_url": "/t4/d",
 			"db_url": "/t4/d",
@@ -219,7 +219,7 @@
 			"display_name": "ffead-cpp [pg-raw-async-prof-b]",
 			"display_name": "ffead-cpp [pg-raw-async-prof-b]",
 			"notes": "async memory libpq batch patch profiled",
 			"notes": "async memory libpq batch patch profiled",
 			"versus": "",
 			"versus": "",
-			"tags": []
+			"tags": ["broken"]
 		},
 		},
 		"postgresql-raw-async-clibpqb-pool-profiled": {
 		"postgresql-raw-async-clibpqb-pool-profiled": {
 			"db_url": "/t4/d",
 			"db_url": "/t4/d",
@@ -240,7 +240,7 @@
 			"display_name": "ffead-cpp [pg-raw-async-prof-b-pool]",
 			"display_name": "ffead-cpp [pg-raw-async-prof-b-pool]",
 			"notes": "async memory libpq batch patch profiled",
 			"notes": "async memory libpq batch patch profiled",
 			"versus": "",
 			"versus": "",
-			"tags": []
+			"tags": ["broken"]
 		},
 		},
 		"postgresql-raw-async-qw-profiled": {
 		"postgresql-raw-async-qw-profiled": {
 			"db_url": "/t5/d",
 			"db_url": "/t5/d",
@@ -282,7 +282,7 @@
 			"display_name": "ffead-cpp [pg-raw-async-qw-prof-b]",
 			"display_name": "ffead-cpp [pg-raw-async-qw-prof-b]",
 			"notes": "async memory libpq batch patch profiled",
 			"notes": "async memory libpq batch patch profiled",
 			"versus": "",
 			"versus": "",
-			"tags": []
+			"tags": ["broken"]
 		},
 		},
 		"postgresql-raw-async-qw-pool-profiled-m": {
 		"postgresql-raw-async-qw-pool-profiled-m": {
 			"query_url": "/t5/quem?queries=",
 			"query_url": "/t5/quem?queries=",
@@ -304,4 +304,4 @@
 			"tags": []
 			"tags": []
 		}
 		}
 	}]
 	}]
-}
+}

+ 38 - 0
frameworks/C++/just-boost/README.md

@@ -0,0 +1,38 @@
+# Just.Boost Benchmarking Test
+
+## Description
+
+Backend using just C++(20) and Boost.
+
+## Run Test
+
+    cd FrameworkBenchmarks/
+    ./tfb --mode verify --test just-boost
+
+## Software Versions
+
+- [Alpine 3.18](https://hub.docker.com/_/alpine)
+- [gcc](https://gcc.gnu.org/)
+- [c++20](https://en.cppreference.com/w/cpp/20)
+- [Boost](https://www.boost.org/)
+	- [Beast](https://www.boost.org/doc/libs/1_83_0/libs/beast/doc/html/index.html) ([HTTP Server with C++ 20 coroutine](https://www.boost.org/doc/libs/1_83_0/libs/beast/example/http/server/awaitable/http_server_awaitable.cpp))
+	- [JSON](https://www.boost.org/doc/libs/1_83_0/libs/json/doc/html/index.html)
+- [libpq — C Library](https://www.postgresql.org/docs/current/libpq.html) (PostgreSQL client)
+
+## Test URLs
+
+### Test 1: JSON Encoding
+
+    http://localhost:8000/json
+
+### Test 2: Single Row Query
+
+    http://localhost:8000/db
+
+### Test 3: Multi Row Query
+
+    http://localhost:8000/queries/{count}
+
+### Test 6: Plaintext
+
+    http://localhost:8000/plaintext

+ 27 - 0
frameworks/C++/just-boost/benchmark_config.json

@@ -0,0 +1,27 @@
+{
+  "framework": "just-boost",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries/",
+        "port": 8000,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "Boost",
+        "language": "C++",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "just-boost",
+        "notes": "",
+        "versus": ""
+      }
+    }
+  ]
+}

+ 25 - 0
frameworks/C++/just-boost/just-boost.dockerfile

@@ -0,0 +1,25 @@
+# docker build --progress=plain --build-arg CXXFLAGS="-Wall" -t just-boost -f just-boost.dockerfile .
+# docker run --rm --name just-boost -p 8000:8000 -d just-boost
+# docker container stop just-boost
+FROM alpine:3.19
+
+ARG APP=just-boost
+ARG CXXFLAGS=-O3
+
+ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
+ENV BCPP_PG_CONN_STR="postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world"
+#ENV BCPP_N_THREADS=0 # default 0 : number of cores
+
+WORKDIR /usr/src/${APP}
+
+RUN apk add --no-cache build-base boost-dev libpq-dev
+COPY *.cpp ./
+RUN g++ ${CXXFLAGS} -std=c++20 \
+        -I$(pg_config --includedir) \
+        -o main main.cpp \
+        -L$(pg_config --libdir) -lpq \
+        && rm *.cpp
+
+EXPOSE 8000
+
+CMD ["./main"]

+ 401 - 0
frameworks/C++/just-boost/main.cpp

@@ -0,0 +1,401 @@
+#include <boost/beast/core.hpp>
+#include <boost/beast/http.hpp>
+#include <boost/beast/version.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/awaitable.hpp>
+#include <boost/asio/co_spawn.hpp>
+#include <boost/asio/use_awaitable.hpp>
+#include <boost/config.hpp>
+#include <boost/json/src.hpp>
+
+#include <algorithm>
+#include <cstdlib>
+#include <ctime>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <thread>
+#include <vector>
+#include <random>
+
+#if defined(BOOST_ASIO_HAS_CO_AWAIT)
+
+#include <libpq-fe.h>
+
+namespace beast = boost::beast;   // from <boost/beast.hpp>
+namespace http = beast::http;     // from <boost/beast/http.hpp>
+namespace net = boost::asio;      // from <boost/asio.hpp>
+using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
+namespace json = boost::json;     // from <boost/json.hpp>
+
+using tcp_stream = typename beast::tcp_stream::rebind_executor<
+		net::use_awaitable_t<>::executor_with_default<net::any_io_executor>>::other;
+
+namespace becpp
+{
+
+using result_ptr = std::unique_ptr<PGresult, decltype(&PQclear)>;
+
+//https://gist.github.com/ictlyh/12fe787ec265b33fd7e4b0bd08bc27cb
+result_ptr prepare(PGconn* conn,
+                   const char* stmtName,
+                   const char* command,
+                   uint8_t nParams)
+{
+	auto res = PQprepare(conn, stmtName, command, nParams, nullptr);
+	if (PQresultStatus(res) != PGRES_COMMAND_OK)
+	{
+		std::cerr << "PQprepare failed: " << PQresultErrorMessage(res)
+			        << std::endl;
+		PQclear(res);
+		return {nullptr, nullptr};
+	}
+
+	return {res, &PQclear};
+}
+
+result_ptr execute(PGconn* conn,
+                   const char* stmtName,
+                   uint8_t nParams,
+                   const char* const* paramValues,
+                   const int* paramLengths = nullptr,
+                   const int* paramFormats = nullptr,
+                   int resultFormat = 0)
+{
+	auto res = PQexecPrepared(conn, stmtName, nParams, paramValues, paramLengths,
+	                          paramFormats, resultFormat);
+	const auto status = PQresultStatus(res);
+	if (status != PGRES_COMMAND_OK &&
+	    status != PGRES_TUPLES_OK  &&
+	    status != PGRES_SINGLE_TUPLE)
+	{
+		std::cerr << "PQexecPrepared failed: " << PQresultErrorMessage(res)
+		          << std::endl;
+		PQclear(res);
+		return {nullptr, nullptr};
+	}
+
+	return {res, &PQclear};
+}
+
+const char* env(const char* env_var, const char* default_value)
+{
+	if (const char* env_p = std::getenv(env_var))
+		return env_p;
+
+	return default_value;
+}
+
+std::string now_string()
+{
+	std::time_t time = std::time(nullptr);
+	char timeString[std::size("Wed, 17 Apr 2013 12:00:00 GMT")];
+	std::strftime(std::data(timeString), std::size(timeString), "%a, %d %b %Y %X %Z", std::localtime(&time));
+	return timeString;
+}
+
+template <class Body, class Allocator>
+http::message_generator
+handle_error(
+	http::request<Body, http::basic_fields<Allocator>>&& req,
+	http::status status,
+	beast::string_view msg)
+{
+	http::response<http::string_body> res{status, req.version()};
+	res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+	res.set(http::field::content_type, "text/html");
+	res.set(http::field::date, now_string());
+	res.keep_alive(req.keep_alive());
+	res.body() = std::string(msg);
+	res.prepare_payload();
+	return res;
+}
+
+template <class Body, class Allocator>
+http::message_generator
+handle_target(
+	http::request<Body, http::basic_fields<Allocator>>&& req,
+	PGconn* conn = nullptr)
+{
+	//std::cout << "handle_target: " << req.target() << std::endl;
+	http::response<http::string_body> res{http::status::ok, req.version()};
+	res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
+	res.set(http::field::content_type, "application/json");
+	res.set(http::field::date, now_string());
+	res.keep_alive(req.keep_alive());
+	
+	if (req.target() == "/json")
+	{
+		// {"message":"Hello, World!"}
+		json::object obj;
+		obj["message"] = "Hello, World!";
+		res.body() = json::serialize(obj);
+	}
+	else if (req.target() == "/plaintext")
+	{
+		res.set(http::field::content_type, "text/plain");
+		res.body() = "Hello, World!";
+	}
+	else if (req.target() == "/db" || req.target().starts_with("/queries/"))
+	{
+		static std::random_device rd;
+		static std::minstd_rand gen(rd());
+		static std::uniform_int_distribution<> distrib(1, 10000);
+		static const char* word_query = "SELECT randomNumber FROM world WHERE id=$1";
+		thread_local auto stmt = prepare(conn, "word_query_stmt", word_query, 1);
+
+		if (req.target() == "/db")
+		{
+			const unsigned uint_id = distrib(gen);
+			const auto str_id =  std::to_string(uint_id);
+			const char* char_ptr_id = str_id.c_str();
+			if (auto rs = execute(conn, "word_query_stmt", 1, &char_ptr_id))
+			{
+				// {"id":3217,"randomNumber":2149}
+				json::object obj;
+				obj["id"] = uint_id;
+				obj["randomNumber"] = std::atoi(PQgetvalue(rs.get(), 0, 0));
+				res.body() = json::serialize(obj);
+				//std::cout << "res.body(): " << res.body() << std::endl;
+			}
+			else
+				return handle_error(std::move(req),
+				                    http::status::internal_server_error,
+				                    "internal_server_error");
+		}
+		else if (req.target().starts_with("/queries/"))
+		{
+			int n_queries = 1;
+			try
+			{
+				const int n = std::stoi(req.target().substr(req.target().find_last_of('/')+1));
+				if (n > 1) n_queries = n;
+			} catch(...) {}
+			if (n_queries > 500) n_queries = 500;
+			json::array objs;
+			for (auto i = 0; i < n_queries; ++i)
+			{
+				const unsigned uint_id = distrib(gen);
+				const auto str_id =  std::to_string(uint_id);
+				const char* char_ptr_id = str_id.c_str();
+				if (auto rs = execute(conn, "word_query_stmt", 1, &char_ptr_id))
+				{
+					// {"id":3217,"randomNumber":2149}
+					json::object obj;
+					obj["id"] = uint_id;
+					obj["randomNumber"] = std::atoi(PQgetvalue(rs.get(), 0, 0));
+					objs.push_back(obj);
+				}
+				else
+					return handle_error(std::move(req),
+					                    http::status::internal_server_error,
+					                    "internal_server_error");
+			}
+			res.body() = json::serialize(objs);
+			//std::cout << "res.body(): " << res.body() << std::endl;
+		}
+	}
+	else
+	{
+		return handle_error(std::move(req),
+		                    http::status::not_found,
+		                    "Unhandled target: '" + std::string(req.target()) + "'");
+	}
+
+	res.prepare_payload();
+	return res;
+}
+
+// Return a response for the given request.
+//
+// The concrete type of the response message (which depends on the
+// request), is type-erased in message_generator.
+template <class Body, class Allocator>
+http::message_generator
+handle_request(
+	http::request<Body, http::basic_fields<Allocator>>&& req)
+{
+	// Make sure we can handle the method
+	if (req.method() != http::verb::get)
+		return handle_error(std::move(req),
+		                    http::status::not_found,
+		                    "Unhandled method: '" + std::string(req.method_string()) + "'");
+
+	if (req.target() == "/json" || req.target() == "/plaintext")
+		return handle_target(std::move(req));
+
+	thread_local std::unique_ptr<PGconn, decltype(&PQfinish)> conn
+	{
+		PQconnectdb(env("BCPP_PG_CONN_STR", "")),
+		&PQfinish
+	};
+	if (PQstatus(conn.get()) != CONNECTION_OK)
+	{
+		auto msg = PQerrorMessage(conn.get());
+		std::cerr << "PQerrorMessage: " << msg << std::endl;
+		return handle_error(std::move(req),
+		                    http::status::internal_server_error,
+		                    msg);
+	}
+	return handle_target(std::move(req), conn.get());
+}
+
+}
+//------------------------------------------------------------------------------
+
+
+// Handles an HTTP server connection
+net::awaitable<void>
+do_session(tcp_stream stream)
+{
+	// This buffer is required to persist across reads
+	beast::flat_buffer buffer;
+
+	// This lambda is used to send messages
+	try
+	{
+		for(;;)
+		{
+			// Set the timeout.
+			stream.expires_after(std::chrono::seconds(30));
+
+			// Read a request
+			http::request<http::string_body> req;
+			co_await http::async_read(stream, buffer, req);
+
+			// Handle the request
+			http::message_generator msg =
+				becpp::handle_request(std::move(req));
+
+			// Determine if we should close the connection
+			bool keep_alive = msg.keep_alive();
+
+			// Send the response
+			co_await beast::async_write(stream, std::move(msg), net::use_awaitable);
+
+			if(! keep_alive)
+			{
+				// This means we should close the connection, usually because
+				// the response indicated the "Connection: close" semantic.
+				break;
+			}
+		}
+	}
+	catch (boost::system::system_error & se)
+	{
+		if (se.code() != http::error::end_of_stream )
+			throw ;
+	}
+
+	// Send a TCP shutdown
+	beast::error_code ec;
+	stream.socket().shutdown(tcp::socket::shutdown_send, ec);
+
+	// At this point the connection is closed gracefully
+	// we ignore the error because the client might have
+	// dropped the connection already.
+}
+
+//------------------------------------------------------------------------------
+
+// Accepts incoming connections and launches the sessions
+net::awaitable<void>
+do_listen(tcp::endpoint endpoint)
+{
+	// Open the acceptor
+	auto acceptor = net::use_awaitable.as_default_on(tcp::acceptor(co_await net::this_coro::executor));
+	acceptor.open(endpoint.protocol());
+
+	// Allow address reuse
+	acceptor.set_option(net::socket_base::reuse_address(true));
+
+	// Bind to the server address
+	acceptor.bind(endpoint);
+
+	// Start listening for connections
+	acceptor.listen(net::socket_base::max_listen_connections);
+
+	for(;;)
+		boost::asio::co_spawn(
+			acceptor.get_executor(),
+				do_session(tcp_stream(co_await acceptor.async_accept())),
+				[](std::exception_ptr e)
+				{
+					if (e)
+						try
+						{
+							std::rethrow_exception(e);
+						}
+						catch (std::exception&){}
+//						catch (std::exception &e) {
+//							std::cerr << "Error in session: " << e.what() << "\n";
+//						}
+				});
+
+}
+
+int main(int argc, char* argv[])
+{
+	auto const address = net::ip::make_address(becpp::env("BCPP_ADDRESS", "0.0.0.0"));
+	auto const port = static_cast<unsigned short>(std::atoi(becpp::env("BCPP_PORT", "8000")));
+	auto env_threads = std::atoi(becpp::env("BCPP_N_THREADS", "0"));
+	if (env_threads == 0)
+	{
+		env_threads = std::thread::hardware_concurrency();
+		std::cout << "Using number of cores: " << env_threads << '\n';
+	}
+	auto const threads = std::max<int>(1, env_threads);
+
+	std::cout << "__GNUG__=" << __GNUG__ << '\n';
+	std::cout << "__cplusplus=" << __cplusplus << '\n';
+	std::cout << "__TIMESTAMP__=" << __TIMESTAMP__ << '\n';
+	std::cout << "__GNUC_EXECUTION_CHARSET_NAME=" << __GNUC_EXECUTION_CHARSET_NAME << '\n';
+	std::cout << "Listening " << address << ':' << port << " threads=" << threads << std::endl;
+
+	// The io_context is required for all I/O
+	net::io_context ioc{threads};
+
+	// Spawn a listening port
+	boost::asio::co_spawn(ioc,
+						  do_listen(tcp::endpoint{address, port}),
+						  [](std::exception_ptr e)
+						  {
+							  if (e)
+								  try
+								  {
+									  std::rethrow_exception(e);
+								  }
+								  catch(std::exception & e)
+								  {
+									  std::cerr << "Error in acceptor: " << e.what() << "\n";
+								  }
+						  });
+
+	// Run the I/O service on the requested number of threads
+	try {
+	std::vector<std::thread> v;
+	v.reserve(threads - 1);
+	for(auto i = threads - 1; i > 0; --i)
+		v.emplace_back(
+		[&ioc]
+		{
+			ioc.run();
+		});
+	ioc.run();
+	} catch (std::exception& e)
+	{
+		std::cerr << "Error in main: " << e.what() << "\n";
+	}
+
+	return EXIT_SUCCESS;
+}
+
+#else
+
+int main(int, char * [])
+{
+	std::printf("awaitables require C++20\n");
+	return 1;
+}
+
+#endif

+ 0 - 48
frameworks/C++/lithium/benchmark_config.json

@@ -27,12 +27,10 @@
         "versus": "None"
         "versus": "None"
       },
       },
       "postgres": {
       "postgres": {
-        "json_url"       : "/json",
         "db_url"         : "/db",
         "db_url"         : "/db",
         "query_url"      : "/queries?N=",
         "query_url"      : "/queries?N=",
         "fortune_url"    : "/fortunes",
         "fortune_url"    : "/fortunes",
         "update_url"     : "/updates?N=",
         "update_url"     : "/updates?N=",
-        "plaintext_url"  : "/plaintext",
         "cached_query_url": "/cached-worlds?N=",
         "cached_query_url": "/cached-worlds?N=",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
@@ -51,12 +49,10 @@
         "versus": "None"
         "versus": "None"
       },
       },
       "postgres-beta": {
       "postgres-beta": {
-        "json_url"       : "/json",
         "db_url"         : "/db",
         "db_url"         : "/db",
         "query_url"      : "/queries?N=",
         "query_url"      : "/queries?N=",
         "fortune_url"    : "/fortunes",
         "fortune_url"    : "/fortunes",
         "update_url"     : "/updates?N=",
         "update_url"     : "/updates?N=",
-        "plaintext_url"  : "/plaintext",
         "cached_query_url": "/cached-worlds?N=",
         "cached_query_url": "/cached-worlds?N=",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
@@ -73,51 +69,7 @@
         "display_name": "Lithium-postgres-beta",
         "display_name": "Lithium-postgres-beta",
         "notes": "",
         "notes": "",
         "versus": "None"
         "versus": "None"
-      },
-      "postgres-batch": {
-        "db_url"         : "/db",
-        "query_url"      : "/queries?N=",
-        "fortune_url"    : "/fortunes",
-        "update_url"     : "/updates?N=",
-        "port": 8080,
-        "approach": "Realistic",
-        "classification": "Micro",
-        "database": "Postgres",
-        "framework": "Lithium",
-        "language": "C++",
-        "flavor": "None",
-        "orm": "Full",
-        "platform": "None",
-        "webserver": "None",
-        "os": "Linux",
-        "database_os": "Linux",
-        "display_name": "Lithium-postgres-batch",
-        "notes": "",
-        "versus": "None"
-      },
-
-      "postgres-batch-beta": {
-        "db_url"         : "/db",
-        "query_url"      : "/queries?N=",
-        "fortune_url"    : "/fortunes",
-        "update_url"     : "/updates?N=",
-        "port": 8080,
-        "approach": "Realistic",
-        "classification": "Micro",
-        "database": "Postgres",
-        "framework": "Lithium",
-        "language": "C++",
-        "flavor": "None",
-        "orm": "Full",
-        "platform": "None",
-        "webserver": "None",
-        "os": "Linux",
-        "database_os": "Linux",
-        "display_name": "Lithium-postgres-batch-beta",
-        "notes": "",
-        "versus": "None"
       }
       }
-
     }
     }
   ]
   ]
 }
 }

+ 3 - 1
frameworks/C++/poco/poco.dockerfile

@@ -9,7 +9,9 @@ ENV POCO_VERSION 1.6.1
 ENV POCO_HOME /poco
 ENV POCO_HOME /poco
 
 
 WORKDIR ${POCO_HOME}
 WORKDIR ${POCO_HOME}
-RUN curl -sL http://pocoproject.org/releases/poco-${POCO_VERSION}/poco-${POCO_VERSION}-all.tar.gz | tar xz --strip-components=1
+RUN wget https://pocoproject.org/releases/poco-${POCO_VERSION}/poco-${POCO_VERSION}-all.zip
+RUN unzip poco-${POCO_VERSION}-all.zip
+RUN mv ./poco-${POCO_VERSION}-all/* ./
 
 
 RUN ./configure --no-tests --no-samples
 RUN ./configure --no-tests --no-samples
 RUN make --quiet PageCompiler-libexec XML-libexec JSON-libexec
 RUN make --quiet PageCompiler-libexec XML-libexec JSON-libexec

+ 9 - 0
frameworks/C++/reactor/README.md

@@ -0,0 +1,9 @@
+# [reactor](https://github.com/shaovie/reactor) (c++) Benchmarking Test
+
+This is the go portion of a [benchmarking test suite](https://www.techempower.com/benchmarks/) comparing a variety of web development platforms.
+
+"reactor is a high-performance, lightweight, i/o event-driven network framework in c++."
+
+## Test URLs
+
+    http://localhost:8080/plaintext

+ 23 - 0
frameworks/C++/reactor/benchmark_config.json

@@ -0,0 +1,23 @@
+{
+  "framework": "reactor",
+  "tests": [{
+    "default": {
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Stripped",
+      "classification": "Platform",
+      "database": "None",
+      "framework": "reactor",
+      "language": "C++",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Reactor",
+      "notes": "",
+      "versus": "c++"
+    }
+  }]
+}

+ 3 - 5
frameworks/Erlang/elli/config.toml → frameworks/C++/reactor/config.toml

@@ -1,16 +1,14 @@
 [framework]
 [framework]
-name = "elli"
+name = "reactor"
 
 
 [main]
 [main]
 urls.plaintext = "/plaintext"
 urls.plaintext = "/plaintext"
-urls.json = "/json"
-urls.db = "/db"
 approach = "Stripped"
 approach = "Stripped"
 classification = "Platform"
 classification = "Platform"
-database = "MySQL"
+database = "None"
 database_os = "Linux"
 database_os = "Linux"
 os = "Linux"
 os = "Linux"
 orm = "Raw"
 orm = "Raw"
 platform = "None"
 platform = "None"
 webserver = "None"
 webserver = "None"
-versus = ""
+versus = "c++"

+ 37 - 0
frameworks/C++/reactor/reactor.dockerfile

@@ -0,0 +1,37 @@
+FROM ubuntu:22.04
+MAINTAINER [email protected]
+
+RUN apt-get update -yqq
+RUN apt-get install -yqq g++-11 gcc-11 make git
+
+RUN   update-alternatives --quiet --remove-all gcc \
+    ; update-alternatives --quiet --remove-all g++ \
+    ; update-alternatives --quiet --remove-all cc \
+    ; update-alternatives --quiet --remove-all cpp \
+    ; update-alternatives --quiet --install /usr/bin/gcc gcc /usr/bin/gcc-11 20 \
+    ; update-alternatives --quiet --install /usr/bin/cc cc /usr/bin/gcc-11 20 \
+    ; update-alternatives --quiet --install /usr/bin/g++ g++ /usr/bin/g++-11 20 \
+    ; update-alternatives --quiet --install /usr/bin/cpp cpp /usr/bin/g++-11 20 \
+    ; update-alternatives --quiet --config gcc \
+    ; update-alternatives --quiet --config cc \
+    ; update-alternatives --quiet --config g++ \
+    ; update-alternatives --quiet --config cpp
+
+
+WORKDIR /reactor-bench
+
+RUN git clone https://github.com/shaovie/reactor.git
+
+
+RUN cd reactor/ && make clean all
+COPY ./techempower.cpp /reactor-bench/reactor
+
+WORKDIR /reactor-bench/reactor
+RUN g++ techempower.cpp -O2 -std=c++11 -lreactor -L./bin -lpthread -o app
+
+ENV LD_LIBRARY_PATH=./bin:$LD_LIBRARY_PATH
+
+EXPOSE 8080
+
+RUN ulimit -n 100000
+CMD ./app

+ 120 - 0
frameworks/C++/reactor/techempower.cpp

@@ -0,0 +1,120 @@
+#include "src/reactor.h"
+#include "src/io_handle.h"
+#include "src/acceptor.h"
+#include "src/options.h"
+#include "src/poll_sync_opt.h"
+
+#include <string>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <thread>
+
+reactor *conn_reactor = nullptr;
+const char httpheaders1[] = "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nServer: goev\r\nContent-Type: text/plain\r\nDate: ";
+const char httpheaders2[] = "\r\nContent-Length: 13\r\n\r\nHello, World!";
+
+const int pcache_data_t = 1;
+
+class http : public io_handle {
+public:
+    virtual bool on_open() {
+        // add_ev_handler 尽量放在最后, (on_open 和o_read可能不在一个线程)
+        if (conn_reactor->add_ev_handler(this, this->get_fd(), ev_handler::ev_read) != 0) {
+            fprintf(stderr, "add new conn to poller fail! %s\n", strerror(errno));
+            return false;
+        }
+        return true;
+    }
+    virtual bool on_read() {
+        char *buf = nullptr;
+        int ret = this->recv(buf);
+        if (ret == 0) // closed
+            return false;
+        else if (ret < 0)
+            return true;
+
+        if (::strstr(buf, "\r\n\r\n") == nullptr) {
+            // invliad msg
+            return false;
+        }
+
+        int writen = 0;
+        ::memcpy(buf, httpheaders1, sizeof(httpheaders1)-1);
+        writen += sizeof(httpheaders1)-1;
+
+        char *date = (char *)this->poll_cache_get(pcache_data_t);
+        ret = ::strlen(date);
+        ::memcpy(buf + writen, date, ret);
+        writen += ret;
+
+        ret = sizeof(httpheaders2)-1;
+        ::memcpy(buf + writen, httpheaders2, ret);
+        writen += ret;
+
+        this->send(buf, writen);
+        return true;
+    }
+    virtual void on_close() {
+        this->destroy();
+    }
+};
+ev_handler *gen_http() {
+    return new http();
+}
+void release_dates(void *p) {
+    delete[] (char *)p;
+}
+void sync_date(bool init) {
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    struct tm tmv;
+    ::localtime_r(&(now.tv_sec), &tmv);
+    char dates[32] = {0};
+    ::strftime(dates, 32, "%a, %d %b %Y %H:%M:%S GMT", &tmv);
+    void **args = new void *[conn_reactor->get_poller_num()];
+    for (int i = 0; i < conn_reactor->get_poller_num(); ++i) {
+        char *ds = new char[32]{0};
+        ::strcpy(ds, dates);
+        poll_sync_opt::sync_cache *arg = new poll_sync_opt::sync_cache();
+        arg->id = pcache_data_t;
+        arg->value = ds;
+        arg->free_func = release_dates;
+        args[i] = arg;
+    }
+    if (init)
+        conn_reactor->init_poll_sync_opt(poll_sync_opt::sync_cache_t, args);
+    else
+        conn_reactor->poll_sync_opt(poll_sync_opt::sync_cache_t, args);
+}
+void sync_date_timing() {
+    while (true) {
+        std::this_thread::sleep_for(std::chrono::seconds(1));
+        sync_date(false);
+    }
+}
+int main (int argc, char *argv[]) {
+    options opt;
+    opt.set_cpu_affinity = true;
+    if (argc > 1)
+        opt.poller_num = atoi(argv[1]);
+
+    signal(SIGPIPE ,SIG_IGN);
+
+    conn_reactor = new reactor();
+    if (conn_reactor->open(opt) != 0)
+        ::exit(1);
+
+    sync_date(true);
+
+    std::thread thr(sync_date_timing);
+    thr.detach();
+
+    opt.reuse_addr = true;
+    acceptor acc(conn_reactor, gen_http);
+    if (acc.open(":8080", opt) != 0)
+        ::exit(1);
+    conn_reactor->run();
+}

+ 6 - 6
frameworks/C++/suil/benchmark/src/main.cpp

@@ -58,19 +58,19 @@ int main(int argc, char *argv[])
 
 
     Endpoint<SystemAttrs, PgSqlMiddleware> ep{"/",
     Endpoint<SystemAttrs, PgSqlMiddleware> ep{"/",
           opt(serverConfig, std::move(config)),
           opt(serverConfig, std::move(config)),
-          opt(numberOfWorkers, 0)   /* Will run with number of available cores */
+          opt(connectionTimeout, 60_sec)
     };
     };
 
 
     ep.middleware<PgSqlMiddleware>().setup(
     ep.middleware<PgSqlMiddleware>().setup(
             suil::env("POSTGRES_CONN", DEFAULT_POSTGRES_CONN),
             suil::env("POSTGRES_CONN", DEFAULT_POSTGRES_CONN),
-            opt(ASYNC,   true),   // connections are async
-            opt(TIMEOUT, 5_sec),  // timeout on db transactions
-            opt(EXPIRES, 30_sec)  // connections are cached for 30 seconds
+            opt(ASYNC,   true),    // connections are async
+            opt(TIMEOUT, 10_sec),  // timeout on db transactions
+            opt(EXPIRES, 30_sec)   // connections are cached for 30 seconds
     );
     );
 
 
 #if SUIL_BENCH_DEV == 1
 #if SUIL_BENCH_DEV == 1
     {
     {
-        scoped(conn, ep.middleware<PgSqlMiddleware>().conn());
+        scoped(conn, ep.middleware<PgSqlMiddleware>().conn(false));
         seedDatabase(conn);
         seedDatabase(conn);
     }
     }
 #endif
 #endif
@@ -152,4 +152,4 @@ int main(int argc, char *argv[])
     });
     });
 
 
     return ep.start();
     return ep.start();
-}
+}

+ 1 - 1
frameworks/C++/suil/suil.dockerfile

@@ -2,7 +2,7 @@ FROM suilteam/base:alpine
 
 
 COPY ./ suil-bench
 COPY ./ suil-bench
 
 
-ENV SUIL_VERSION=0.1.0
+ENV SUIL_VERSION=0.1.1
 ENV SUIL_VERSION_TAG=alpha
 ENV SUIL_VERSION_TAG=alpha
 ENV SUIL_CONFIGURATION=Release
 ENV SUIL_CONFIGURATION=Release
 
 

+ 0 - 4
frameworks/C++/treefrog/benchmark_config.json

@@ -26,12 +26,10 @@
       "versus": ""
       "versus": ""
     },
     },
     "mysql": {
     "mysql": {
-      "json_url": "/json/json",
       "db_url": "/world/random",
       "db_url": "/world/random",
       "query_url": "/world/queries/",
       "query_url": "/world/queries/",
       "fortune_url": "/fortune/index",
       "fortune_url": "/fortune/index",
       "update_url": "/world/updates/",
       "update_url": "/world/updates/",
-      "plaintext_url": "/world/plain",
       "cached_query_url": "/world/cached_queries/",
       "cached_query_url": "/world/cached_queries/",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",
@@ -73,12 +71,10 @@
       "versus": ""
       "versus": ""
     },
     },
     "mongodb": {
     "mongodb": {
-      "json_url": "/json/json",
       "db_url": "/world/mrandom",
       "db_url": "/world/mrandom",
       "query_url": "/world/mqueries/",
       "query_url": "/world/mqueries/",
       "fortune_url": "/fortune/mindex",
       "fortune_url": "/fortune/mindex",
       "update_url": "/world/mupdates/",
       "update_url": "/world/mupdates/",
-      "plaintext_url": "/world/plain",
       "cached_query_url": "/world/cached_mqueries/",
       "cached_query_url": "/world/cached_mqueries/",
       "port": 8080,
       "port": 8080,
       "approach": "Realistic",
       "approach": "Realistic",

+ 47 - 15
frameworks/C++/treefrog/config/application.ini

@@ -34,12 +34,18 @@ UploadTemporaryDirectory=tmp
 # Specify setting files for SQL databases.
 # Specify setting files for SQL databases.
 SqlDatabaseSettingsFiles=database.ini
 SqlDatabaseSettingsFiles=database.ini
 
 
-# Specify the setting file for MongoDB, mongodb.ini.
+# Specify the setting file for MongoDB.
+# To access MongoDB server, uncomment the following line.
 MongoDbSettingsFile=mongodb.ini
 MongoDbSettingsFile=mongodb.ini
 
 
-# Specify the setting file for Redis, redis.ini.
+# Specify the setting file for Redis.
+# To access Redis server, uncomment the following line.
 RedisSettingsFile=
 RedisSettingsFile=
 
 
+# Specify the setting file for Memcached.
+# To access Memcached server, uncomment the following line.
+MemcachedSettingsFile=
+
 # Specify the directory path to store SQL query files.
 # Specify the directory path to store SQL query files.
 SqlQueriesStoredDirectory=sql/
 SqlQueriesStoredDirectory=sql/
 
 
@@ -51,7 +57,24 @@ DirectViewRenderMode=false
 # Specify a file path for SQL query log.
 # Specify a file path for SQL query log.
 # If it's empty or the line is commented out, output to SQL query log
 # If it's empty or the line is commented out, output to SQL query log
 # is disabled.
 # is disabled.
-SqlQueryLogFile=
+SqlQueryLog.FilePath=
+
+# Specify the layout of SQL query log.
+#  %d : date-time
+#  %p : priority (lowercase)
+#  %P : priority (uppercase)
+#  %t : thread ID (dec)
+#  %T : thread ID (hex)
+#  %i : PID (dec)
+#  %I : PID (hex)
+#  %e : elapsed processing time in milliseconds
+#  %m : log message
+#  %n : newline code
+SqlQueryLog.Layout="%d [%t] (%e) %m%n"
+
+# Specify the date-time format of SQL query log, see also QDateTime
+# class reference.
+SqlQueryLog.DateTimeFormat="yyyy-MM-dd hh:mm:ss"
 
 
 # Determines whether the application aborts (to create a core dump
 # Determines whether the application aborts (to create a core dump
 # on Unix systems) or not when it output a fatal message by tFatal()
 # on Unix systems) or not when it output a fatal message by tFatal()
@@ -95,6 +118,7 @@ Session.Name=TFSESSION
 # For 'sqlobject', the settings specified in SqlDatabaseSettingsFiles are used.
 # For 'sqlobject', the settings specified in SqlDatabaseSettingsFiles are used.
 # For 'mongodb', the settings specified in MongoDbSettingsFile are used.
 # For 'mongodb', the settings specified in MongoDbSettingsFile are used.
 # For 'redis', the settings specified in RedisSettingsFile are used.
 # For 'redis', the settings specified in RedisSettingsFile are used.
+# For 'memcached', the settings specified in MemcachedSettingsFile are used.
 Session.StoreType=cookie
 Session.StoreType=cookie
 
 
 # Replaces the session ID with a new one each time one connects, and
 # Replaces the session ID with a new one each time one connects, and
@@ -111,6 +135,10 @@ Session.CookieDomain=
 # Specifies a path attribute to set in the session cookie. Defaults to /.
 # Specifies a path attribute to set in the session cookie. Defaults to /.
 Session.CookiePath=/
 Session.CookiePath=/
 
 
+# Specifies a value to assert that a cookie must not be sent with cross-origin
+# requests; Strict, Lax or None.
+Session.CookieSameSite=Lax
+
 # Probability that the garbage collection starts.
 # Probability that the garbage collection starts.
 # If 100 specified, the GC of sessions starts at the rate of once per 100
 # If 100 specified, the GC of sessions starts at the rate of once per 100
 # accesses. If 0 specified, the GC never starts.
 # accesses. If 0 specified, the GC never starts.
@@ -138,7 +166,7 @@ MPM.thread.MaxAppServers=
 # Maximum number of action threads allowed to start simultaneously
 # Maximum number of action threads allowed to start simultaneously
 # per server process. Set max_connections parameter of the DBMS
 # per server process. Set max_connections parameter of the DBMS
 # to (MaxAppServers * MaxThreadsPerAppServer) or more.
 # to (MaxAppServers * MaxThreadsPerAppServer) or more.
-MPM.thread.MaxThreadsPerAppServer=100
+MPM.thread.MaxThreadsPerAppServer=128
 
 
 ##
 ##
 ## MPM epoll section
 ## MPM epoll section
@@ -182,6 +210,7 @@ AccessLog.FilePath=
 #  %r : First line of request
 #  %r : First line of request
 #  %s : Status code
 #  %s : Status code
 #  %O : Bytes sent, including headers, cannot be zero
 #  %O : Bytes sent, including headers, cannot be zero
+#  %e : elapsed processing time in milliseconds
 #  %n : Newline code
 #  %n : Newline code
 AccessLog.Layout="%h %d \"%r\" %s %O%n"
 AccessLog.Layout="%h %d \"%r\" %s %O%n"
 
 
@@ -210,13 +239,13 @@ ActionMailer.smtp.HostName=
 # Specify the connection's port number.
 # Specify the connection's port number.
 ActionMailer.smtp.Port=
 ActionMailer.smtp.Port=
 
 
-# Enables STARTTLS extension if true.
-ActionMailer.smtp.EnableSTARTTLS=false
-
 # Enables SMTP authentication if true; disables SMTP
 # Enables SMTP authentication if true; disables SMTP
 # authentication if false.
 # authentication if false.
 ActionMailer.smtp.Authentication=false
 ActionMailer.smtp.Authentication=false
 
 
+# Enables STARTTLS extension if true.
+ActionMailer.smtp.EnableSTARTTLS=false
+
 # Specify the user name for SMTP authentication.
 # Specify the user name for SMTP authentication.
 ActionMailer.smtp.UserName=
 ActionMailer.smtp.UserName=
 
 
@@ -237,18 +266,21 @@ ActionMailer.smtp.DelayedDelivery=false
 ## Cache section
 ## Cache section
 ##
 ##
 
 
-# Specify the settings file to enable the cache module.
-# Comment out the following line.
+# Specify the settings file to enable the cache module,
+# which can be used through Tf::cache() function.
+# See https://api-reference.treefrogframework.org/classTCache.html.
+# Uncomment the following line and write connection information
+# to the file.
 Cache.SettingsFile=cache.ini
 Cache.SettingsFile=cache.ini
 
 
-# Specify the cache backend, such as 'sqlite', 'mongodb'
-# or 'redis'.
-Cache.Backend=sqlite
+# Specify the cache backend, such as 'sqlite', 'mongodb', 'redis',
+# 'memcached' or 'memory'.
+Cache.Backend=memory
 
 
 # Probability of starting garbage collection (GC) for cache.
 # Probability of starting garbage collection (GC) for cache.
-# If 100 is specified, GC will be started at a rate of once per 100
+# If 1000 is specified, GC will be started at a rate of once per 1000
 # sets. If 0 is specified, the GC never starts.
 # sets. If 0 is specified, the GC never starts.
-Cache.GcProbability=10000000
+Cache.GcProbability=100000000
 
 
 # If true, enable LZ4 compression when storing data.
 # If true, enable LZ4 compression when storing data.
-Cache.EnableCompression=no
+Cache.EnableCompression=false

+ 10 - 0
frameworks/C++/treefrog/config/cache.ini

@@ -28,3 +28,13 @@ UserName=
 Password=
 Password=
 ConnectOptions=
 ConnectOptions=
 PostOpenStatements=
 PostOpenStatements=
+
+[memory]
+DatabaseName=tfcache.shm
+HostName=
+Port=
+UserName=
+Password=
+ConnectOptions=MEMORY_SIZE=1G
+PostOpenStatements=
+

+ 34 - 2
frameworks/C++/treefrog/controllers/worldcontroller.cpp

@@ -3,6 +3,7 @@
 #include "pworld.h"
 #include "pworld.h"
 #include "mngworld.h"
 #include "mngworld.h"
 #include <TCache>
 #include <TCache>
+#include <TSqlQuery>
 
 
 
 
 void WorldController::index()
 void WorldController::index()
@@ -220,7 +221,7 @@ void WorldController::cached_pqueries()
 
 
 void WorldController::cached_pqueries(const QString &num)
 void WorldController::cached_pqueries(const QString &num)
 {
 {
-    constexpr int SECONDS = 60 * 10;  // cache time
+    constexpr int SECONDS = 60 * 30;  // cache time
     QVariantList worlds;
     QVariantList worlds;
     QVariantMap world;
     QVariantMap world;
     int d = std::min(std::max(num.toInt(), 1), 500);
     int d = std::min(std::max(num.toInt(), 1), 500);
@@ -246,17 +247,48 @@ void WorldController::cached_pqueries(const QString &num)
 
 
 void WorldController::pupdates(const QString &num)
 void WorldController::pupdates(const QString &num)
 {
 {
+    const QString statement("UPDATE world SET randomnumber = CASE id");
     QVariantList worlds;
     QVariantList worlds;
+    QString ids;
+    QString q = statement;
+    q.reserve(4096);
     int d = std::min(std::max(num.toInt(), 1), 500);
     int d = std::min(std::max(num.toInt(), 1), 500);
     PWorld world;
     PWorld world;
 
 
+    auto blkupdate = [&q, &ids, &statement]() {
+        if (!ids.isEmpty()) {
+            ids.chop(1);
+            q += QStringLiteral(" END WHERE id IN (%1)").arg(ids);
+            TSqlQuery query;
+            query.exec(q);
+            ids.clear();
+            q = statement;
+        }
+    };
+
     for (int i = 0; i < d; ++i) {
     for (int i = 0; i < d; ++i) {
         int id = Tf::random(1, 10000);
         int id = Tf::random(1, 10000);
         world = PWorld::get(id);
         world = PWorld::get(id);
         world.setRandomNumber( Tf::random(1, 10000) );
         world.setRandomNumber( Tf::random(1, 10000) );
-        world.update();
+        q += QLatin1String(" WHEN ");
+        q += QString::number(world.id());
+        q += QLatin1String(" THEN ");
+        q += QString::number(world.randomNumber());
+        ids += QString::number(world.id());
+        ids += ',';
         worlds << world.toVariantMap();
         worlds << world.toVariantMap();
+
+        if (!((i + 1) % 200)) {
+            blkupdate();
+        }
     }
     }
+
+    if (d == 1) {
+        world.update();
+    } else {
+        blkupdate();
+    }
+
     renderJson(worlds);
     renderJson(worlds);
 }
 }
 
 

+ 10 - 10
frameworks/C++/treefrog/treefrog-epoll.dockerfile

@@ -1,21 +1,21 @@
-FROM buildpack-deps:focal
+FROM buildpack-deps:jammy
 
 
 ENV DEBIAN_FRONTEND noninteractive
 ENV DEBIAN_FRONTEND noninteractive
 ENV DEBCONF_NOWARNINGS yes
 ENV DEBCONF_NOWARNINGS yes
-ENV TFVER=2.2.0
+ENV TFVER=2.7.1
 
 
-RUN apt-get update -yqq && apt-get upgrade -yq && apt-get install -yqq --no-install-recommends \
-    software-properties-common unzip wget make cmake gcc clang libjemalloc-dev qt5-qmake qt5-default qtbase5-dev \
-    qtbase5-dev-tools libqt5sql5 libqt5sql5-mysql libqt5sql5-psql libqt5qml5 libqt5xml5 \
-    qtdeclarative5-dev libqt5quick5 libqt5quickparticles5 libqt5gui5 libqt5printsupport5 \
-    libqt5widgets5 libqt5opengl5-dev libqt5quicktest5 libqt5sql5-sqlite libsqlite3-dev libmongoc-dev libbson-dev \
-    redis-server
+RUN apt-get update -yqq && apt-get upgrade -yq && \
+    apt-get install -yqq --no-install-recommends software-properties-common unzip wget libjemalloc-dev \
+    qmake6 qt6-base-dev qt6-base-dev-tools qt6-tools-dev-tools qt6-declarative-dev libqt6sql6-mysql \
+    libqt6sql6-psql libqt6sql6-odbc libqt6sql6-sqlite libqt6core6 libqt6qml6 libqt6xml6 libpq5 libodbc1 \
+    libmongoc-dev libbson-dev gcc g++ clang make cmake pkg-config
+RUN rm -f /usr/bin/qmake; ln -sf /usr/bin/qmake6 /usr/bin/qmake
 
 
 WORKDIR /usr/src
 WORKDIR /usr/src
 RUN wget -q https://github.com/treefrogframework/treefrog-framework/archive/v${TFVER}.tar.gz
 RUN wget -q https://github.com/treefrogframework/treefrog-framework/archive/v${TFVER}.tar.gz
 RUN tar xf v${TFVER}.tar.gz
 RUN tar xf v${TFVER}.tar.gz
 RUN cd treefrog-framework-${TFVER} && \
 RUN cd treefrog-framework-${TFVER} && \
-    ./configure --spec=linux-clang && \
+    ./configure --enable-shared-mongoc --spec=linux-clang && \
     cd src && \
     cd src && \
     make -j4 && \
     make -j4 && \
     make install && \
     make install && \
@@ -37,4 +37,4 @@ RUN sed -i 's|MultiProcessingModule=.*|MultiProcessingModule=epoll|g' config/app
 EXPOSE 8080
 EXPOSE 8080
 
 
 # 3. Start TreeFrog
 # 3. Start TreeFrog
-CMD treefrog /workspace
+CMD treefrog /workspace

+ 9 - 9
frameworks/C++/treefrog/treefrog-mongodb.dockerfile

@@ -1,21 +1,21 @@
-FROM buildpack-deps:focal
+FROM buildpack-deps:jammy
 
 
 ENV DEBIAN_FRONTEND noninteractive
 ENV DEBIAN_FRONTEND noninteractive
 ENV DEBCONF_NOWARNINGS yes
 ENV DEBCONF_NOWARNINGS yes
-ENV TFVER=2.2.0
+ENV TFVER=2.7.1
 
 
-RUN apt-get update -yqq && apt-get upgrade -yq && apt-get install -yqq --no-install-recommends \
-    software-properties-common unzip wget make cmake gcc clang libjemalloc-dev qt5-qmake qt5-default qtbase5-dev \
-    qtbase5-dev-tools libqt5sql5 libqt5sql5-mysql libqt5sql5-psql libqt5qml5 libqt5xml5 \
-    qtdeclarative5-dev libqt5quick5 libqt5quickparticles5 libqt5gui5 libqt5printsupport5 \
-    libqt5widgets5 libqt5opengl5-dev libqt5quicktest5 libqt5sql5-sqlite libsqlite3-dev libmongoc-dev libbson-dev \
-    redis-server
+RUN apt-get update -yqq && apt-get upgrade -yq && \
+    apt-get install -yqq --no-install-recommends software-properties-common unzip wget libjemalloc-dev \
+    qmake6 qt6-base-dev qt6-base-dev-tools qt6-tools-dev-tools qt6-declarative-dev libqt6sql6-mysql \
+    libqt6sql6-psql libqt6sql6-odbc libqt6sql6-sqlite libqt6core6 libqt6qml6 libqt6xml6 libpq5 libodbc1 \
+    libmongoc-dev libbson-dev gcc g++ clang make cmake pkg-config
+RUN rm -f /usr/bin/qmake; ln -sf /usr/bin/qmake6 /usr/bin/qmake
 
 
 WORKDIR /usr/src
 WORKDIR /usr/src
 RUN wget -q https://github.com/treefrogframework/treefrog-framework/archive/v${TFVER}.tar.gz
 RUN wget -q https://github.com/treefrogframework/treefrog-framework/archive/v${TFVER}.tar.gz
 RUN tar xf v${TFVER}.tar.gz
 RUN tar xf v${TFVER}.tar.gz
 RUN cd treefrog-framework-${TFVER} && \
 RUN cd treefrog-framework-${TFVER} && \
-    ./configure --spec=linux-clang && \
+    ./configure --enable-shared-mongoc --spec=linux-clang && \
     cd src && \
     cd src && \
     make -j4 && \
     make -j4 && \
     make install && \
     make install && \

+ 9 - 9
frameworks/C++/treefrog/treefrog-mysql.dockerfile

@@ -1,21 +1,21 @@
-FROM buildpack-deps:focal
+FROM buildpack-deps:jammy
 
 
 ENV DEBIAN_FRONTEND noninteractive
 ENV DEBIAN_FRONTEND noninteractive
 ENV DEBCONF_NOWARNINGS yes
 ENV DEBCONF_NOWARNINGS yes
-ENV TFVER=2.2.0
+ENV TFVER=2.7.1
 
 
-RUN apt-get update -yqq && apt-get upgrade -yq && apt-get install -yqq --no-install-recommends \
-    software-properties-common unzip wget make cmake gcc clang libjemalloc-dev qt5-qmake qt5-default qtbase5-dev \
-    qtbase5-dev-tools libqt5sql5 libqt5sql5-mysql libqt5sql5-psql libqt5qml5 libqt5xml5 \
-    qtdeclarative5-dev libqt5quick5 libqt5quickparticles5 libqt5gui5 libqt5printsupport5 \
-    libqt5widgets5 libqt5opengl5-dev libqt5quicktest5 libqt5sql5-sqlite libsqlite3-dev libmongoc-dev libbson-dev \
-    redis-server
+RUN apt-get update -yqq && apt-get upgrade -yq && \
+    apt-get install -yqq --no-install-recommends software-properties-common unzip wget libjemalloc-dev \
+    qmake6 qt6-base-dev qt6-base-dev-tools qt6-tools-dev-tools qt6-declarative-dev libqt6sql6-mysql \
+    libqt6sql6-psql libqt6sql6-odbc libqt6sql6-sqlite libqt6core6 libqt6qml6 libqt6xml6 libpq5 libodbc1 \
+    libmongoc-dev libbson-dev gcc g++ clang make cmake pkg-config
+RUN rm -f /usr/bin/qmake; ln -sf /usr/bin/qmake6 /usr/bin/qmake
 
 
 WORKDIR /usr/src
 WORKDIR /usr/src
 RUN wget -q https://github.com/treefrogframework/treefrog-framework/archive/v${TFVER}.tar.gz
 RUN wget -q https://github.com/treefrogframework/treefrog-framework/archive/v${TFVER}.tar.gz
 RUN tar xf v${TFVER}.tar.gz
 RUN tar xf v${TFVER}.tar.gz
 RUN cd treefrog-framework-${TFVER} && \
 RUN cd treefrog-framework-${TFVER} && \
-    ./configure --spec=linux-clang && \
+    ./configure --enable-shared-mongoc --spec=linux-clang && \
     cd src && \
     cd src && \
     make -j4 && \
     make -j4 && \
     make install && \
     make install && \

+ 9 - 9
frameworks/C++/treefrog/treefrog.dockerfile

@@ -1,21 +1,21 @@
-FROM buildpack-deps:focal
+FROM buildpack-deps:jammy
 
 
 ENV DEBIAN_FRONTEND noninteractive
 ENV DEBIAN_FRONTEND noninteractive
 ENV DEBCONF_NOWARNINGS yes
 ENV DEBCONF_NOWARNINGS yes
-ENV TFVER=2.2.0
+ENV TFVER=2.7.1
 
 
-RUN apt-get update -yqq && apt-get upgrade -yq && apt-get install -yqq --no-install-recommends \
-    software-properties-common unzip wget make cmake gcc clang libjemalloc-dev qt5-qmake qt5-default qtbase5-dev \
-    qtbase5-dev-tools libqt5sql5 libqt5sql5-mysql libqt5sql5-psql libqt5qml5 libqt5xml5 \
-    qtdeclarative5-dev libqt5quick5 libqt5quickparticles5 libqt5gui5 libqt5printsupport5 \
-    libqt5widgets5 libqt5opengl5-dev libqt5quicktest5 libqt5sql5-sqlite libsqlite3-dev libmongoc-dev libbson-dev \
-    redis-server
+RUN apt-get update -yqq && apt-get upgrade -yq && \
+    apt-get install -yqq --no-install-recommends software-properties-common unzip wget libjemalloc-dev \
+    qmake6 qt6-base-dev qt6-base-dev-tools qt6-tools-dev-tools qt6-declarative-dev libqt6sql6-mysql \
+    libqt6sql6-psql libqt6sql6-odbc libqt6sql6-sqlite libqt6core6 libqt6qml6 libqt6xml6 libpq5 libodbc1 \
+    libmongoc-dev libbson-dev gcc g++ clang make cmake pkg-config
+RUN rm -f /usr/bin/qmake; ln -sf /usr/bin/qmake6 /usr/bin/qmake
 
 
 WORKDIR /usr/src
 WORKDIR /usr/src
 RUN wget -q https://github.com/treefrogframework/treefrog-framework/archive/v${TFVER}.tar.gz
 RUN wget -q https://github.com/treefrogframework/treefrog-framework/archive/v${TFVER}.tar.gz
 RUN tar xf v${TFVER}.tar.gz
 RUN tar xf v${TFVER}.tar.gz
 RUN cd treefrog-framework-${TFVER} && \
 RUN cd treefrog-framework-${TFVER} && \
-    ./configure --spec=linux-clang && \
+    ./configure --enable-shared-mongoc --spec=linux-clang && \
     cd src && \
     cd src && \
     make -j4 && \
     make -j4 && \
     make install && \
     make install && \

+ 16 - 8
frameworks/C++/ulib/benchmark_config.json

@@ -16,7 +16,8 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "ULib",
       "display_name": "ULib",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "",
+      "tags": ["broken"]
     },
     },
     "plaintext_fit": {
     "plaintext_fit": {
       "plaintext_url": "/plaintext",
       "plaintext_url": "/plaintext",
@@ -33,7 +34,8 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "ULib-fit",
       "display_name": "ULib-fit",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "",
+      "tags": ["broken"]
     },
     },
     "json": {
     "json": {
       "json_url": "/json",
       "json_url": "/json",
@@ -50,7 +52,8 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "ULib",
       "display_name": "ULib",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "",
+      "tags": ["broken"]
     },
     },
     "json_fit": {
     "json_fit": {
       "json_url": "/json",
       "json_url": "/json",
@@ -67,7 +70,8 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "ULib-fit",
       "display_name": "ULib-fit",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "",
+      "tags": ["broken"]
     },
     },
     "mysql": {
     "mysql": {
       "db_url": "/db",
       "db_url": "/db",
@@ -87,7 +91,8 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "ULib-mysql",
       "display_name": "ULib-mysql",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "",
+      "tags": ["broken"]
     },
     },
     "postgres": {
     "postgres": {
       "db_url": "/db",
       "db_url": "/db",
@@ -107,7 +112,8 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "ULib-postgres",
       "display_name": "ULib-postgres",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "",
+      "tags": ["broken"]
     },
     },
     "postgres_fit": {
     "postgres_fit": {
       "db_url": "/db",
       "db_url": "/db",
@@ -125,7 +131,8 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "ULib-fit",
       "display_name": "ULib-fit",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "",
+      "tags": ["broken"]
     },
     },
     "mongodb": {
     "mongodb": {
       "setup_file": "setup_mongodb",
       "setup_file": "setup_mongodb",
@@ -146,7 +153,8 @@
       "database_os": "Linux",
       "database_os": "Linux",
       "display_name": "ULib-mongodb",
       "display_name": "ULib-mongodb",
       "notes": "",
       "notes": "",
-      "versus": ""
+      "versus": "",
+      "tags": ["broken"]
     }
     }
   }]
   }]
 }
 }

+ 42 - 0
frameworks/C++/userver/README.md

@@ -0,0 +1,42 @@
+# userver Benchmarking Test
+
+This is the [userver](https://github.com/userver-framework/userver) portion of a [benchmarking test suite](https://github.com/TechEmpower/FrameworkBenchmarks) comparing a variety of web development platforms.
+
+This benchmarks comes in two configurations: **userver** and **userver-bare**, where both configurations use exactly the same handlers code, but **userver-bare** replaces default http implementation of **userver** with custom one.  
+You see, **userver** being feature-rich framework widely used in production comes with a lot of useful functionality built-in (metrics, dynamic configuring, logging/tracing, congestion control etc...) none of which is of any use in benchmarks; although most of that can be disabled via configs, some parts remain, and these parts aren't free.  
+The aim of **userver-bare** is to explore practical limits of lower-level **userver** functionality when performance is an absolute must, while still being idiomatic userver code.
+
+### Test Type Implementation Source Code
+
+* [Plaintext](userver_benchmark/controllers/plaintext/handler.cpp)
+* [Json](userver_benchmark/controllers/json/handler.cpp)
+* [Single Database Query](userver_benchmark/controllers/single_query/handler.cpp)
+* [Multiple Database Queries](userver_benchmark/controllers/multiple_queries/handler.cpp)
+* [Database Updates](userver_benchmark/controllers/updates/handler.cpp)
+* [Cached Queries](userver_benchmark/controllers/cached_queries/handler.cpp)
+
+## Test URLs
+### Plaintext
+
+http://localhost:8080/plaintext
+
+### Json
+
+http://localhost:8080/json
+
+### Single Database Query
+
+http://localhost:8080/db
+
+### Multiple Database Queries
+
+http://localhost:8080/queries
+
+### Database Updates
+
+http://localhost:8080/updates
+
+### Cached Queries
+
+http://localhost:8080/cached-queries
+

+ 55 - 0
frameworks/C++/userver/benchmark_config.json

@@ -0,0 +1,55 @@
+{
+  "framework": "userver",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "cached_query_url": "/cached-queries?count=",
+	"fortune_url": "/fortunes",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Fullstack",
+        "database": "postgres",
+        "framework": "userver",
+        "language": "C++",
+        "flavor": "None",
+        "orm": "Micro",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "userver",
+        "notes": "",
+        "versus": "None"
+      },
+      "bare": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "cached_query_url": "/cached-queries?count=",
+	"fortune_url": "/fortunes",
+        "port": 8081,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "postgres",
+        "framework": "userver",
+        "language": "C++",
+        "flavor": "None",
+        "orm": "Micro",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "userver[bare]",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 14 - 10
frameworks/C++/wt/config.toml → frameworks/C++/userver/config.toml

@@ -1,5 +1,5 @@
 [framework]
 [framework]
-name = "wt"
+name = "userver"
 
 
 [main]
 [main]
 urls.plaintext = "/plaintext"
 urls.plaintext = "/plaintext"
@@ -7,28 +7,32 @@ urls.json = "/json"
 urls.db = "/db"
 urls.db = "/db"
 urls.query = "/queries?queries="
 urls.query = "/queries?queries="
 urls.update = "/updates?queries="
 urls.update = "/updates?queries="
-urls.fortune = "/fortune"
+urls.cached_query = "/cached-queries?count="
+urls.fortune = "/fortunes"
 approach = "Realistic"
 approach = "Realistic"
 classification = "Fullstack"
 classification = "Fullstack"
-database = "MySQL"
+database = "Postgres"
 database_os = "Linux"
 database_os = "Linux"
 os = "Linux"
 os = "Linux"
-orm = "Full"
+orm = "Micro"
 platform = "None"
 platform = "None"
 webserver = "None"
 webserver = "None"
-versus = "wt"
+versus = "None"
 
 
-[postgres]
+[bare]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
 urls.db = "/db"
 urls.db = "/db"
 urls.query = "/queries?queries="
 urls.query = "/queries?queries="
 urls.update = "/updates?queries="
 urls.update = "/updates?queries="
-urls.fortune = "/fortune"
+urls.cached_query = "/cached-queries?count="
+urls.fortune = "/fortunes"
 approach = "Realistic"
 approach = "Realistic"
-classification = "Fullstack"
+classification = "Micro"
 database = "Postgres"
 database = "Postgres"
 database_os = "Linux"
 database_os = "Linux"
 os = "Linux"
 os = "Linux"
-orm = "Full"
+orm = "Micro"
 platform = "None"
 platform = "None"
 webserver = "None"
 webserver = "None"
-versus = "wt"
+versus = "None"

+ 27 - 0
frameworks/C++/userver/userver-bare.dockerfile

@@ -0,0 +1,27 @@
+FROM ghcr.io/userver-framework/ubuntu-userver-build-base:v1 AS builder
+
+RUN apt update && \
+    apt install -y lsb-release wget software-properties-common gnupg && \
+        wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && ./llvm.sh 16
+
+WORKDIR /src
+RUN git clone https://github.com/userver-framework/userver.git && \
+    cd userver && git checkout b85d540d7022e344f6fcf9fd467c67b046c961fe
+COPY userver_benchmark/ ./
+RUN mkdir build && cd build && \
+    cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -DUSERVER_FEATURE_CRYPTOPP_BLAKE2=0 \
+          -DUSERVER_FEATURE_UTEST=0 \
+          -DUSERVER_FEATURE_POSTGRESQL=1 \
+          -DUSERVER_FEATURE_ERASE_LOG_WITH_LEVEL=warning \
+          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native" -DCMAKE_C_FLAGS="-march=native" \
+          -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 -DUSERVER_LTO_CACHE=0 .. && \
+    make -j $(nproc)
+
+FROM builder AS runner
+WORKDIR /app
+COPY userver_configs/* ./
+COPY --from=builder /src/build/userver_techempower ./
+
+EXPOSE 8081
+CMD ./userver_techempower -c ./static_config.yaml
+

+ 27 - 0
frameworks/C++/userver/userver.dockerfile

@@ -0,0 +1,27 @@
+FROM ghcr.io/userver-framework/ubuntu-userver-build-base:v1 AS builder
+
+RUN apt update && \
+    apt install -y lsb-release wget software-properties-common gnupg && \
+        wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && ./llvm.sh 16
+
+WORKDIR /src
+RUN git clone https://github.com/userver-framework/userver.git && \
+    cd userver && git checkout b85d540d7022e344f6fcf9fd467c67b046c961fe
+COPY userver_benchmark/ ./
+RUN mkdir build && cd build && \
+    cmake -DUSERVER_IS_THE_ROOT_PROJECT=0 -DUSERVER_FEATURE_CRYPTOPP_BLAKE2=0 \
+          -DUSERVER_FEATURE_UTEST=0 \
+          -DUSERVER_FEATURE_POSTGRESQL=1 \
+          -DUSERVER_FEATURE_ERASE_LOG_WITH_LEVEL=warning \
+          -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-march=native" -DCMAKE_C_FLAGS="-march=native" \
+          -DCMAKE_CXX_COMPILER=clang++-16 -DCMAKE_C_COMPILER=clang-16 -DUSERVER_USE_LD=lld-16 -DUSERVER_LTO_CACHE=0 .. && \
+    make -j $(nproc)
+
+FROM builder AS runner
+WORKDIR /app
+COPY userver_configs/* ./
+COPY --from=builder /src/build/userver_techempower ./
+
+EXPOSE 8080
+CMD ./userver_techempower -c ./static_config.yaml
+

+ 3 - 0
frameworks/C++/userver/userver_benchmark/.clang-format

@@ -0,0 +1,3 @@
+BasedOnStyle: google
+DerivePointerAlignment: false
+IncludeBlocks: Preserve

+ 18 - 0
frameworks/C++/userver/userver_benchmark/CMakeLists.txt

@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.12)
+project(userver_techempower CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+
+file(GLOB_RECURSE SOURCES
+  ${CMAKE_CURRENT_SOURCE_DIR}/controllers/*.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/common/*.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/bare/*.cpp
+)
+
+include(GNUInstallDirs)
+
+add_subdirectory(userver)
+userver_setup_environment()
+
+add_executable(${PROJECT_NAME} ${SOURCES} userver_techempower.cpp)
+target_link_libraries(${PROJECT_NAME} PRIVATE userver-core userver-postgresql)

+ 205 - 0
frameworks/C++/userver/userver_benchmark/bare/simple_connection.cpp

@@ -0,0 +1,205 @@
+#include "simple_connection.hpp"
+
+#include <array>
+
+#include <cctz/time_zone.h>
+#include <http_parser.h>
+#include <boost/container/small_vector.hpp>
+
+#include "simple_server.hpp"
+
+#include <userver/engine/async.hpp>
+#include <userver/utils/datetime/wall_coarse_clock.hpp>
+#include <userver/utils/scope_guard.hpp>
+#include <userver/utils/small_string.hpp>
+
+namespace userver_techempower::bare {
+
+namespace {
+
+struct HttpParser final {
+  http_parser parser{};
+  http_parser_settings parser_settings{};
+
+  std::function<void(std::string_view)> on_request_cb{};
+
+  userver::utils::SmallString<50> url;
+
+  explicit HttpParser(std::function<void(std::string_view)> on_request_cb)
+      : on_request_cb{std::move(on_request_cb)} {
+    http_parser_init(&parser, HTTP_REQUEST);
+    parser.data = this;
+
+    http_parser_settings_init(&parser_settings);
+    parser_settings.on_url = HttpOnUrl;
+    parser_settings.on_message_begin = HttpOnMessageBegin;
+    parser_settings.on_message_complete = HttpOnMessageComplete;
+  }
+
+  void Execute(const char* data, std::size_t length) {
+    http_parser_execute(&parser, &parser_settings, data, length);
+  }
+
+  static int HttpOnUrl(http_parser* parser, const char* data,
+                       std::size_t length) {
+    auto* self = static_cast<HttpParser*>(parser->data);
+    self->url.append(std::string_view{data, length});
+    return 0;
+  }
+
+  static int HttpOnMessageBegin(http_parser* parser) {
+    auto* self = static_cast<HttpParser*>(parser->data);
+    self->url.clear();
+    return 0;
+  }
+
+  static int HttpOnMessageComplete(http_parser* parser) {
+    auto* self = static_cast<HttpParser*>(parser->data);
+    self->on_request_cb(static_cast<std::string_view>(self->url));
+    return 0;
+  }
+};
+
+class ResponseBuffers final {
+ public:
+  using HeadersString = userver::utils::SmallString<200>;
+
+  HeadersString& Next(userver::engine::io::Socket& socket, std::string&& body) {
+    if (Size() == kMaxResponses) {
+      Send(socket);
+    }
+
+    auto& response = responses_.emplace_back();
+    response.body = std::move(body);
+    return response.headers;
+  }
+
+  void Send(userver::engine::io::Socket& socket) {
+    if (Size() == 0) {
+      return;
+    }
+
+    boost::container::small_vector<userver::engine::io::IoData,
+                                   kMaxResponses * 2>
+        iovec(Size() * 2);
+
+    std::size_t index = 0;
+    std::size_t total_size = 0;
+    for (const auto& response : responses_) {
+      iovec[index++] = {response.headers.data(), response.headers.size()};
+      iovec[index++] = {response.body.data(), response.body.size()};
+      total_size += response.headers.size() + response.body.size();
+    }
+
+    if (socket.SendAll(iovec.data(), iovec.size(), {}) != total_size) {
+      throw std::runtime_error{"Socket closed by remote"};
+    }
+
+    responses_.clear();
+  }
+
+ private:
+  static constexpr std::size_t kMaxResponses = 16;
+
+  [[nodiscard]] std::size_t Size() const { return responses_.size(); }
+
+  struct Response final {
+    HeadersString headers;
+    std::string body;
+  };
+
+  boost::container::small_vector<Response, kMaxResponses> responses_;
+};
+
+constexpr std::string_view kCommonHeaders{"HTTP/1.1 200 OK\r\nServer: us\r\n"};
+constexpr std::string_view kHeadersEnd{"\r\n\r\n"};
+
+std::string MakeHttpDate(std::chrono::system_clock::time_point date) {
+  static const std::string kFormatString = "%a, %d %b %Y %H:%M:%S %Z";
+  static const auto tz = cctz::utc_time_zone();
+
+  return cctz::format(kFormatString, date, tz);
+}
+
+std::string_view GetCachedDate() {
+  constexpr size_t kMaxDateHeaderLength = 128;
+
+  static thread_local std::chrono::seconds::rep last_second = 0;
+  static thread_local char last_time_string[kMaxDateHeaderLength]{};
+  static thread_local std::string_view result_view{};
+
+  const auto now = userver::utils::datetime::WallCoarseClock::now();
+  const auto now_seconds =
+      std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch())
+          .count();
+  if (now_seconds != last_second) {
+    last_second = now_seconds;
+
+    const auto time_str = MakeHttpDate(now);
+
+    std::memcpy(last_time_string, time_str.c_str(), time_str.size());
+    result_view = std::string_view{last_time_string, time_str.size()};
+  }
+
+  return result_view;
+}
+
+}  // namespace
+
+SimpleConnection::SimpleConnection(SimpleServer& server,
+                                   userver::engine::io::Socket&& socket)
+    : server_{server},
+      socket_{std::move(socket)},
+      processing_task_{userver::engine::AsyncNoSpan([this] { Process(); })} {}
+
+SimpleConnection::~SimpleConnection() { processing_task_.SyncCancel(); }
+
+void SimpleConnection::Process() {
+  constexpr std::size_t kBufferSize = 4096;
+  std::array<char, kBufferSize> buffer{};
+
+  userver::utils::ScopeGuard close_guard{[this] { socket_.Close(); }};
+
+  ResponseBuffers buffers{};
+  const auto handle_request = [this, &buffers](std::string_view url) {
+    auto response = server_.HandleRequest(url);
+    const auto content_length_str = std::to_string(response.body.size());
+    auto& headers = buffers.Next(socket_, std::move(response.body));
+
+    headers.append(kCommonHeaders);
+    headers.append("Content-Type: ");
+    headers.append(response.content_type);
+
+    headers.append("\r\nContent-Length: ");
+    headers.append(content_length_str);
+
+    headers.append("\r\nDate: ");
+    headers.append(GetCachedDate());
+
+    headers.append(kHeadersEnd);
+  };
+  HttpParser parser{handle_request};
+
+  std::size_t last_bytes_read = 0;
+  while (true) {
+    bool is_readable = true;
+    if (last_bytes_read < kBufferSize) {
+      is_readable = socket_.WaitReadable({});
+    }
+
+    last_bytes_read =
+        is_readable ? socket_.RecvSome(buffer.data(), kBufferSize, {}) : 0;
+    if (last_bytes_read == 0) {
+      break;
+    }
+
+    parser.Execute(buffer.data(), last_bytes_read);
+    if (parser.parser.http_errno != 0) {
+      break;
+    }
+
+    buffers.Send(socket_);
+  }
+}
+
+}  // namespace userver_techempower::bare

+ 26 - 0
frameworks/C++/userver/userver_benchmark/bare/simple_connection.hpp

@@ -0,0 +1,26 @@
+#pragma once
+
+#include <userver/engine/io/socket.hpp>
+#include <userver/engine/task/task_with_result.hpp>
+
+namespace userver_techempower::bare {
+
+class SimpleServer;
+
+class SimpleConnection final {
+ public:
+  explicit SimpleConnection(SimpleServer& server,
+                            userver::engine::io::Socket&& socket);
+  ~SimpleConnection();
+
+ private:
+  void Process();
+
+  const SimpleServer& server_;
+
+  userver::engine::io::Socket socket_;
+
+  userver::engine::TaskWithResult<void> processing_task_;
+};
+
+}  // namespace userver_techempower::bare

+ 12 - 0
frameworks/C++/userver/userver_benchmark/bare/simple_response.hpp

@@ -0,0 +1,12 @@
+#pragma once
+
+#include <string>
+
+namespace userver_techempower::bare {
+
+struct SimpleResponse final {
+  std::string body;
+  std::string content_type;
+};
+
+}  // namespace userver_techempower::bare

+ 90 - 0
frameworks/C++/userver/userver_benchmark/bare/simple_router.cpp

@@ -0,0 +1,90 @@
+#include "simple_router.hpp"
+
+#include <userver/components/component_context.hpp>
+
+#include "../controllers/cached_queries/handler.hpp"
+#include "../controllers/fortunes/handler.hpp"
+#include "../controllers/json/handler.hpp"
+#include "../controllers/multiple_queries/handler.hpp"
+#include "../controllers/plaintext/handler.hpp"
+#include "../controllers/single_query/handler.hpp"
+#include "../controllers/updates/handler.hpp"
+
+namespace userver_techempower::bare {
+
+namespace {
+
+constexpr std::string_view kPlainTextUrlPrefix{"/plaintext"};
+constexpr std::string_view kJsonUrlPrefix{"/json"};
+constexpr std::string_view kSingleQueryUrlPrefix{"/db"};
+constexpr std::string_view kMultipleQueriesUrlPrefix{"/queries"};
+constexpr std::string_view kUpdatesUrlPrefix{"/updates"};
+constexpr std::string_view kCachedQueriesUrlPrefix{"/cached-queries"};
+constexpr std::string_view kFortunesUrlPrefix{"/fortunes"};
+
+// NOLINTNEXTLINE
+const std::string kContentTypePlain{"text/plain"};
+// NOLINTNEXTLINE
+const std::string kContentTypeJson{"application/json"};
+// NOLINTNEXTLINE
+const std::string kContentTypeTextHtml{"text/html; charset=utf-8"};
+
+bool StartsWith(std::string_view source, std::string_view pattern) {
+  return source.substr(0, pattern.length()) == pattern;
+}
+
+}  // namespace
+
+SimpleRouter::SimpleRouter(const userver::components::ComponentConfig& config,
+                           const userver::components::ComponentContext& context)
+    : userver::components::LoggableComponentBase{config, context},
+      single_query_{context.FindComponent<single_query::Handler>()},
+      multiple_queries_{context.FindComponent<multiple_queries::Handler>()},
+      updates_{context.FindComponent<updates::Handler>()},
+      cached_queries_{context.FindComponent<cached_queries::Handler>()},
+      fortunes_{context.FindComponent<fortunes::Handler>()} {}
+
+SimpleRouter::~SimpleRouter() = default;
+
+SimpleResponse SimpleRouter::RouteRequest(std::string_view url) const {
+  if (StartsWith(url, kPlainTextUrlPrefix)) {
+    return {plaintext::Handler::GetResponse(), kContentTypePlain};
+  }
+
+  if (StartsWith(url, kJsonUrlPrefix)) {
+    return {ToString(json::Handler::GetResponse()), kContentTypeJson};
+  }
+
+  if (StartsWith(url, kSingleQueryUrlPrefix)) {
+    return {ToString(single_query_.GetResponse()), kContentTypeJson};
+  }
+
+  if (StartsWith(url, kMultipleQueriesUrlPrefix)) {
+    const auto queries = db_helpers::ParseParamFromQuery(
+        url.substr(kMultipleQueriesUrlPrefix.size()), "queries");
+
+    return {ToString(multiple_queries_.GetResponse(queries)), kContentTypeJson};
+  }
+
+  if (StartsWith(url, kUpdatesUrlPrefix)) {
+    const auto queries = db_helpers::ParseParamFromQuery(
+        url.substr(kMultipleQueriesUrlPrefix.size()), "queries");
+
+    return {ToString(updates_.GetResponse(queries)), kContentTypeJson};
+  }
+
+  if (StartsWith(url, kCachedQueriesUrlPrefix)) {
+    const auto count = db_helpers::ParseParamFromQuery(
+        url.substr(kCachedQueriesUrlPrefix.size()), "count");
+
+    return {ToString(cached_queries_.GetResponse(count)), kContentTypeJson};
+  }
+
+  if (StartsWith(url, kFortunesUrlPrefix)) {
+    return {fortunes_.GetResponse(), kContentTypeTextHtml};
+  }
+
+  throw std::runtime_error{"No handler found for url"};
+}
+
+}  // namespace userver_techempower::bare

+ 46 - 0
frameworks/C++/userver/userver_benchmark/bare/simple_router.hpp

@@ -0,0 +1,46 @@
+#pragma once
+
+#include <userver/components/loggable_component_base.hpp>
+
+#include "simple_response.hpp"
+
+namespace userver_techempower {
+
+namespace single_query {
+class Handler;
+}
+namespace multiple_queries {
+class Handler;
+}
+namespace updates {
+class Handler;
+}
+namespace cached_queries {
+class Handler;
+}
+namespace fortunes {
+class Handler;
+}
+
+namespace bare {
+
+class SimpleRouter final : public userver::components::LoggableComponentBase {
+ public:
+  static constexpr std::string_view kName{"simple-router"};
+
+  SimpleRouter(const userver::components::ComponentConfig& config,
+               const userver::components::ComponentContext& context);
+  ~SimpleRouter() final;
+
+  [[nodiscard]] SimpleResponse RouteRequest(std::string_view url) const;
+
+ private:
+  const single_query::Handler& single_query_;
+  const multiple_queries::Handler& multiple_queries_;
+  const updates::Handler& updates_;
+  const cached_queries::Handler& cached_queries_;
+  const fortunes::Handler& fortunes_;
+};
+
+}  // namespace bare
+}  // namespace userver_techempower

+ 28 - 0
frameworks/C++/userver/userver_benchmark/bare/simple_server.cpp

@@ -0,0 +1,28 @@
+#include "simple_server.hpp"
+
+#include <userver/components/component_context.hpp>
+#include <userver/engine/io/socket.hpp>
+
+#include "simple_connection.hpp"
+#include "simple_router.hpp"
+
+namespace userver_techempower::bare {
+
+SimpleServer::SimpleServer(const userver::components::ComponentConfig& config,
+                           const userver::components::ComponentContext& context)
+    : userver::components::TcpAcceptorBase(config, context),
+      router_{context.FindComponent<SimpleRouter>()} {}
+
+SimpleServer::~SimpleServer() = default;
+
+void SimpleServer::ProcessSocket(userver::engine::io::Socket&& socket) {
+  const auto fd = socket.Fd();
+  connections_[fd] =
+      std::make_unique<SimpleConnection>(*this, std::move(socket));
+}
+
+SimpleResponse SimpleServer::HandleRequest(std::string_view url) const {
+  return router_.RouteRequest(url);
+}
+
+}  // namespace userver_techempower::bare

+ 34 - 0
frameworks/C++/userver/userver_benchmark/bare/simple_server.hpp

@@ -0,0 +1,34 @@
+#pragma once
+
+#include <array>
+#include <memory>
+
+#include <userver/components/tcp_acceptor_base.hpp>
+
+#include "simple_response.hpp"
+
+namespace userver_techempower::bare {
+
+class SimpleConnection;
+class SimpleRouter;
+
+class SimpleServer final : public userver::components::TcpAcceptorBase {
+ public:
+  static constexpr std::string_view kName{"simple-server"};
+
+  SimpleServer(const userver::components::ComponentConfig& config,
+               const userver::components::ComponentContext& context);
+  ~SimpleServer() final;
+
+ private:
+  void ProcessSocket(userver::engine::io::Socket&& socket) final;
+
+  friend class SimpleConnection;
+  [[nodiscard]] SimpleResponse HandleRequest(std::string_view url) const;
+
+  const SimpleRouter& router_;
+
+  static constexpr std::size_t kMaxFd = 65536;
+  std::array<std::unique_ptr<SimpleConnection>, kMaxFd> connections_;
+};
+}  // namespace userver_techempower::bare

+ 80 - 0
frameworks/C++/userver/userver_benchmark/common/db_helpers.cpp

@@ -0,0 +1,80 @@
+#include "db_helpers.hpp"
+
+#include <cctype>
+#include <charconv>
+
+#include <userver/formats/json/inline.hpp>
+#include <userver/utils/rand.hpp>
+
+namespace userver_techempower::db_helpers {
+
+namespace {
+
+int ParseFromQueryVal(std::string_view query_val) {
+  if (query_val.empty()) {
+    return 1;
+  }
+
+  int parse_result{};
+  const auto [ptr, err] = std::from_chars(
+      query_val.data(), query_val.data() + query_val.size(), parse_result);
+  if (err != std::errc{} || ptr != query_val.data() + query_val.size()) {
+    return 1;
+  }
+
+  return std::min(500, std::max(1, parse_result));
+}
+
+}  // namespace
+
+userver::storages::postgres::Query CreateNonLoggingQuery(
+    std::string statement) {
+  return userver::storages::postgres::Query{
+      statement, std::nullopt /* name */,
+      userver::storages::postgres::Query::LogMode::kNameOnly};
+}
+
+int GenerateRandomId() {
+  return userver::utils::RandRange(1, kMaxWorldRows + 1);
+}
+
+int GenerateRandomValue() {
+  return userver::utils::RandRange(1, kMaxWorldRows + 1);
+}
+
+userver::formats::json::Value Serialize(
+    const WorldTableRow& value,
+    userver::formats::serialize::To<userver::formats::json::Value>) {
+  return userver::formats::json::MakeObject("id", value.id, "randomNumber",
+                                            value.random_number);
+}
+
+int ParseParamFromQuery(const userver::server::http::HttpRequest& request,
+                        const std::string& name) {
+  const auto& arg_str = request.GetArg(name);
+  return ParseFromQueryVal(arg_str);
+}
+
+int ParseParamFromQuery(std::string_view url, std::string_view name) {
+  auto pos = url.find(name);
+  if (pos == std::string_view::npos) {
+    return 1;
+  }
+  pos += name.size() + 1;  // +1 for '='
+
+  std::size_t len = 0;
+  while (pos + len < url.size() && std::isdigit(url[pos + len])) {
+    ++len;
+  }
+
+  return ParseFromQueryVal(url.substr(pos, len));
+}
+
+DatabasePoolSemaphore::DatabasePoolSemaphore(std::size_t initial_count)
+    : semaphore_{initial_count} {}
+
+userver::engine::SemaphoreLock DatabasePoolSemaphore::Acquire() const {
+  return userver::engine::SemaphoreLock{semaphore_};
+}
+
+}  // namespace userver_techempower::db_helpers

+ 50 - 0
frameworks/C++/userver/userver_benchmark/common/db_helpers.hpp

@@ -0,0 +1,50 @@
+#pragma once
+
+#include <userver/engine/semaphore.hpp>
+#include <userver/formats/json/value.hpp>
+#include <userver/server/http/http_request.hpp>
+#include <userver/storages/postgres/cluster_types.hpp>
+#include <userver/storages/postgres/query.hpp>
+
+namespace userver_techempower::db_helpers {
+
+userver::storages::postgres::Query CreateNonLoggingQuery(std::string statement);
+
+constexpr int kMaxWorldRows = 10000;
+
+const userver::storages::postgres::Query kSelectRowQuery =
+    CreateNonLoggingQuery("SELECT id, randomNumber FROM World WHERE id = $1");
+
+constexpr auto kClusterHostType =
+    userver::storages::postgres::ClusterHostType::kMaster;
+
+constexpr std::string_view kDbComponentName = "hello-world-db";
+
+struct WorldTableRow final {
+  int id;
+  int random_number;
+};
+
+int GenerateRandomId();
+int GenerateRandomValue();
+
+userver::formats::json::Value Serialize(
+    const WorldTableRow& value,
+    userver::formats::serialize::To<userver::formats::json::Value>);
+
+int ParseParamFromQuery(const userver::server::http::HttpRequest& request,
+                        const std::string& name);
+
+int ParseParamFromQuery(std::string_view url, std::string_view name);
+
+class DatabasePoolSemaphore final {
+ public:
+  explicit DatabasePoolSemaphore(std::size_t initial_count);
+
+  userver::engine::SemaphoreLock Acquire() const;
+
+ private:
+  mutable userver::engine::Semaphore semaphore_;
+};
+
+}  // namespace userver_techempower::db_helpers

+ 37 - 0
frameworks/C++/userver/userver_benchmark/controllers/cached_queries/handler.cpp

@@ -0,0 +1,37 @@
+#include "handler.hpp"
+
+#include <userver/formats/serialize/common_containers.hpp>
+
+#include <boost/container/small_vector.hpp>
+
+namespace userver_techempower::cached_queries {
+
+Handler::Handler(const userver::components::ComponentConfig& config,
+                 const userver::components::ComponentContext& context)
+    : userver::server::handlers::HttpHandlerJsonBase{config, context},
+      cache_{context.FindComponent<WorldCacheComponent>()},
+      query_arg_name_{"count"} {}
+
+userver::formats::json::Value Handler::HandleRequestJsonThrow(
+    const userver::server::http::HttpRequest& request,
+    const userver::formats::json::Value&,
+    userver::server::request::RequestContext&) const {
+  const auto queries =
+      db_helpers::ParseParamFromQuery(request, query_arg_name_);
+
+  return GetResponse(queries);
+}
+
+userver::formats::json::Value Handler::GetResponse(int queries) const {
+  boost::container::small_vector<db_helpers::WorldTableRow, 500> result(
+      queries);
+
+  const auto cache_ptr = cache_.Get();
+  const auto& cache = *cache_ptr;
+  std::generate(result.begin(), result.end(),
+                [&cache] { return cache.at(db_helpers::GenerateRandomId()); });
+
+  return userver::formats::json::ValueBuilder{result}.ExtractValue();
+}
+
+}  // namespace userver_techempower::cached_queries

+ 29 - 0
frameworks/C++/userver/userver_benchmark/controllers/cached_queries/handler.hpp

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <userver/server/handlers/http_handler_json_base.hpp>
+
+#include "world_cache_component.hpp"
+
+namespace userver_techempower::cached_queries {
+
+class Handler final : public userver::server::handlers::HttpHandlerJsonBase {
+ public:
+  static constexpr std::string_view kName = "cached-queries-handler";
+
+  Handler(const userver::components::ComponentConfig& config,
+          const userver::components::ComponentContext& context);
+
+  userver::formats::json::Value HandleRequestJsonThrow(
+      const userver::server::http::HttpRequest& request,
+      const userver::formats::json::Value&,
+      userver::server::request::RequestContext&) const final;
+
+  userver::formats::json::Value GetResponse(int queries) const;
+
+ private:
+  const WorldCacheComponent& cache_;
+
+  const std::string query_arg_name_;
+};
+
+}  // namespace userver_techempower::cached_queries

+ 21 - 0
frameworks/C++/userver/userver_benchmark/controllers/cached_queries/world_cache_component.cpp

@@ -0,0 +1,21 @@
+#include "world_cache_component.hpp"
+
+namespace userver_techempower::cached_queries {
+
+WorldCache::WorldCache() { data_.reserve(db_helpers::kMaxWorldRows + 1); }
+
+std::size_t WorldCache::size() const { return data_.size(); }
+
+void WorldCache::insert_or_assign(int key, db_helpers::WorldTableRow&& row) {
+  if (size() <= static_cast<std::size_t>(key)) {
+    data_.resize(key + 1);
+  }
+
+  data_[key] = row;
+}
+
+const db_helpers::WorldTableRow& WorldCache::at(size_t ind) const {
+  return data_[ind];
+}
+
+}  // namespace userver_techempower::cached_queries

+ 36 - 0
frameworks/C++/userver/userver_benchmark/controllers/cached_queries/world_cache_component.hpp

@@ -0,0 +1,36 @@
+#pragma once
+
+#include <vector>
+
+#include "../../common/db_helpers.hpp"
+
+#include <userver/cache/base_postgres_cache.hpp>
+
+namespace userver_techempower::cached_queries {
+
+class WorldCache final {
+ public:
+  WorldCache();
+
+  std::size_t size() const;
+  void insert_or_assign(int key, db_helpers::WorldTableRow&& row);
+
+  const db_helpers::WorldTableRow& at(size_t ind) const;
+
+ private:
+  std::vector<db_helpers::WorldTableRow> data_;
+};
+
+struct WorldCachePolicy final {
+  static constexpr std::string_view kName = "world-pg-cache";
+  using ValueType = db_helpers::WorldTableRow;
+  using CacheContainer = WorldCache;
+  static constexpr auto kKeyMember = &db_helpers::WorldTableRow::id;
+  static constexpr const char* kQuery = "SELECT id, randomNumber FROM World";
+  static constexpr const char* kUpdatedField = "";
+  static constexpr auto kClusterHostType = db_helpers::kClusterHostType;
+};
+
+using WorldCacheComponent = userver::components::PostgreCache<WorldCachePolicy>;
+
+}  // namespace userver_techempower::cached_queries

+ 166 - 0
frameworks/C++/userver/userver_benchmark/controllers/fortunes/handler.cpp

@@ -0,0 +1,166 @@
+#include "handler.hpp"
+
+#include <vector>
+
+#include <userver/components/component_context.hpp>
+#include <userver/storages/postgres/postgres.hpp>
+
+namespace userver_techempower::fortunes {
+
+namespace {
+
+const std::string kContentTypeHeader{"Content-Type"};
+const std::string kContentTypeTextHtml{"text/html; charset=utf-8"};
+
+struct Fortune final {
+  int id;
+  std::string_view message;
+};
+
+constexpr std::string_view kResultingHtmlHeader{
+    "<!DOCTYPE "
+    "html><html><head><title>Fortunes</title></head><body><table><tr><th>id</"
+    "th><th>message</th></tr>"};
+constexpr std::string_view kResultingHtmlFooter{"</table></body></html>"};
+
+constexpr std::string_view kNewRowStart{"<tr><td>"};
+constexpr std::string_view kColumnsSeparator{"</td><td>"};
+constexpr std::string_view kNewRowEnd{"</td></tr>"};
+
+constexpr std::string_view kEscapedQuote{"&quot;"};
+constexpr std::string_view kEscapedAmpersand{"&amp;"};
+constexpr std::string_view kEscapedLessThanSign{"&lt;"};
+constexpr std::string_view kEscapedMoreThanSign{"&gt;"};
+
+void AppendFortune(std::string& result, const Fortune& fortune) {
+  {
+    auto old_size = result.size();
+    const auto fortune_id = std::to_string(fortune.id);
+
+    const auto first_step_size =
+        kNewRowStart.size() + fortune_id.size() + kColumnsSeparator.size();
+
+    result.resize(old_size + first_step_size);
+    char* append_position = result.data() + old_size;
+
+    // this is just faster than std::string::append if we know the resulting
+    // size upfront, because there are a lot of not inlined calls otherwise
+    const auto append = [&append_position](std::string_view what) {
+      std::memcpy(append_position, what.data(), what.size());
+      append_position += what.size();
+    };
+    append(kNewRowStart);
+    append(fortune_id);
+    append(kColumnsSeparator);
+  }
+
+  {
+    std::string_view message{fortune.message};
+
+    const auto do_append = [&result](std::string_view unescaped,
+                                     std::string_view escaped) {
+      const auto old_size = result.size();
+      const auto added_size = unescaped.size() + escaped.size();
+
+      result.resize(result.size() + added_size);
+      char* append_position = result.data() + old_size;
+      if (!unescaped.empty()) {
+        std::memcpy(append_position, unescaped.data(), unescaped.size());
+        append_position += unescaped.size();
+      }
+      std::memcpy(append_position, escaped.data(), escaped.size());
+    };
+
+    std::size_t unescaped_len = 0;
+    const auto append = [&unescaped_len, &message,
+                         &do_append](std::string_view escaped) {
+      do_append(message.substr(0, unescaped_len), escaped);
+      message = message.substr(std::exchange(unescaped_len, 0) + 1);
+    };
+
+    while (unescaped_len < message.size()) {
+      const auto c = message[unescaped_len];
+      switch (c) {
+        case '"': {
+          append(kEscapedQuote);
+          break;
+        }
+        case '&': {
+          append(kEscapedAmpersand);
+          break;
+        }
+        case '<': {
+          append(kEscapedLessThanSign);
+          break;
+        }
+        case '>': {
+          append(kEscapedMoreThanSign);
+          break;
+        }
+        default:
+          ++unescaped_len;
+      }
+    }
+    result.append(message);
+  }
+
+  { result.append(kNewRowEnd); }
+}
+
+std::string FormatFortunes(const std::vector<Fortune>& fortunes) {
+  std::string result{};
+  // Wild guess, seems reasonable. Could be the exact value needed, but that
+  // looks kinda cheating.
+  result.reserve(2048);
+
+  result.append(kResultingHtmlHeader);
+  for (const auto& fortune : fortunes) {
+    AppendFortune(result, fortune);
+  }
+  result.append(kResultingHtmlFooter);
+
+  return result;
+}
+
+constexpr std::size_t kBestConcurrencyWildGuess = 256;
+
+}  // namespace
+
+Handler::Handler(const userver::components::ComponentConfig& config,
+                 const userver::components::ComponentContext& context)
+    : userver::server::handlers::HttpHandlerBase{config, context},
+      pg_{context
+              .FindComponent<userver::components::Postgres>(
+                  db_helpers::kDbComponentName)
+              .GetCluster()},
+      select_all_fortunes_query_{
+          db_helpers::CreateNonLoggingQuery("SELECT id, message FROM Fortune")},
+      semaphore_{kBestConcurrencyWildGuess} {}
+
+std::string Handler::HandleRequestThrow(
+    const userver::server::http::HttpRequest& request,
+    userver::server::request::RequestContext&) const {
+  request.GetHttpResponse().SetHeader(kContentTypeHeader, kContentTypeTextHtml);
+  return GetResponse();
+}
+
+std::string Handler::GetResponse() const {
+  const auto pg_result = [this] {
+    const auto lock = semaphore_.Acquire();
+    return pg_->Execute(db_helpers::kClusterHostType,
+                        select_all_fortunes_query_);
+  }();
+
+  auto fortunes = pg_result.AsContainer<std::vector<Fortune>>(
+      userver::storages::postgres::kRowTag);
+  fortunes.push_back({0, "Additional fortune added at request time."});
+
+  std::sort(fortunes.begin(), fortunes.end(),
+            [](const auto& lhs, const auto& rhs) {
+              return lhs.message < rhs.message;
+            });
+
+  return FormatFortunes(fortunes);
+}
+
+}  // namespace userver_techempower::fortunes

+ 32 - 0
frameworks/C++/userver/userver_benchmark/controllers/fortunes/handler.hpp

@@ -0,0 +1,32 @@
+#pragma once
+
+#include "../../common/db_helpers.hpp"
+
+#include <userver/server/handlers/http_handler_base.hpp>
+
+#include <userver/storages/postgres/postgres_fwd.hpp>
+#include <userver/storages/postgres/query.hpp>
+
+namespace userver_techempower::fortunes {
+
+class Handler final : public userver::server::handlers::HttpHandlerBase {
+ public:
+  static constexpr std::string_view kName = "fortunes-handler";
+
+  Handler(const userver::components::ComponentConfig& config,
+          const userver::components::ComponentContext& context);
+
+  std::string HandleRequestThrow(
+      const userver::server::http::HttpRequest& request,
+      userver::server::request::RequestContext&) const final;
+
+  std::string GetResponse() const;
+
+ private:
+  const userver::storages::postgres::ClusterPtr pg_;
+  const userver::storages::postgres::Query select_all_fortunes_query_;
+
+  db_helpers::DatabasePoolSemaphore semaphore_;
+};
+
+}  // namespace userver_techempower::fortunes

+ 16 - 0
frameworks/C++/userver/userver_benchmark/controllers/json/handler.cpp

@@ -0,0 +1,16 @@
+#include "handler.hpp"
+
+namespace userver_techempower::json {
+
+userver::formats::json::Value Handler::HandleRequestJsonThrow(
+    const userver::server::http::HttpRequest&,
+    const userver::formats::json::Value&,
+    userver::server::request::RequestContext&) const {
+  return GetResponse();
+}
+
+userver::formats::json::Value Handler::GetResponse() {
+  return userver::formats::json::MakeObject("message", "Hello, World!");
+}
+
+}  // namespace userver_techempower::json

Some files were not shown because too many files changed in this diff