Browse Source

Elixir plug ecto (#5391)

* Add Elixir with Plug and Ecto tests.

* Remove old cowboy folder, elixir-plug-ecto tests the same thing.

* Rollback change to travis test file.
JT Turner 5 years ago
parent
commit
0eeebb9618
41 changed files with 622 additions and 253 deletions
  1. 0 2
      frameworks/Elixir/cowboy/.gitignore
  2. 0 1
      frameworks/Elixir/cowboy/README.md
  3. 0 24
      frameworks/Elixir/cowboy/benchmark_config.json
  4. 0 14
      frameworks/Elixir/cowboy/config/config.exs
  5. 0 12
      frameworks/Elixir/cowboy/elixir-cowboy.dockerfile
  6. 0 2
      frameworks/Elixir/cowboy/lib/hello.ex
  7. 0 39
      frameworks/Elixir/cowboy/lib/hello/application.ex
  8. 0 26
      frameworks/Elixir/cowboy/lib/hello/router.ex
  9. 0 30
      frameworks/Elixir/cowboy/mix.exs
  10. 0 55
      frameworks/Elixir/cowboy/rel/config.exs
  11. 0 3
      frameworks/Elixir/cowboy/rel/plugins/.gitignore
  12. 0 30
      frameworks/Elixir/cowboy/rel/vm.args
  13. 0 15
      frameworks/Elixir/cowboy/setup.sh
  14. 4 0
      frameworks/Elixir/plug/.formatter.exs
  15. 26 0
      frameworks/Elixir/plug/.gitignore
  16. 50 0
      frameworks/Elixir/plug/README.md
  17. 32 0
      frameworks/Elixir/plug/benchmark_config.json
  18. 5 0
      frameworks/Elixir/plug/config/config.exs
  19. 1 0
      frameworks/Elixir/plug/config/dev.exs
  20. 1 0
      frameworks/Elixir/plug/config/prod.exs
  21. 14 0
      frameworks/Elixir/plug/config/releases.exs
  22. 19 0
      frameworks/Elixir/plug/docker-compose.yml
  23. 37 0
      frameworks/Elixir/plug/elixir-plug-ecto.dockerfile
  24. 31 0
      frameworks/Elixir/plug/lib/framework_benchmarks/application.ex
  25. 51 0
      frameworks/Elixir/plug/lib/framework_benchmarks/cached_world.ex
  26. 43 0
      frameworks/Elixir/plug/lib/framework_benchmarks/endpoints.ex
  27. 23 0
      frameworks/Elixir/plug/lib/framework_benchmarks/handlers/cached-world.ex
  28. 18 0
      frameworks/Elixir/plug/lib/framework_benchmarks/handlers/db.ex
  29. 48 0
      frameworks/Elixir/plug/lib/framework_benchmarks/handlers/fortune.ex
  30. 21 0
      frameworks/Elixir/plug/lib/framework_benchmarks/handlers/helpers.ex
  31. 12 0
      frameworks/Elixir/plug/lib/framework_benchmarks/handlers/json.ex
  32. 10 0
      frameworks/Elixir/plug/lib/framework_benchmarks/handlers/plain-text.ex
  33. 33 0
      frameworks/Elixir/plug/lib/framework_benchmarks/handlers/query.ex
  34. 56 0
      frameworks/Elixir/plug/lib/framework_benchmarks/handlers/update.ex
  35. 7 0
      frameworks/Elixir/plug/lib/framework_benchmarks/models/fortune.ex
  36. 7 0
      frameworks/Elixir/plug/lib/framework_benchmarks/models/world.ex
  37. 5 0
      frameworks/Elixir/plug/lib/framework_benchmarks/repo.ex
  38. 34 0
      frameworks/Elixir/plug/mix.exs
  39. 25 0
      frameworks/Elixir/plug/mix.lock
  40. 8 0
      frameworks/Elixir/plug/test/framework_benchmarks_test.exs
  41. 1 0
      frameworks/Elixir/plug/test/test_helper.exs

+ 0 - 2
frameworks/Elixir/cowboy/.gitignore

@@ -1,2 +0,0 @@
-deps/
-_build/

+ 0 - 1
frameworks/Elixir/cowboy/README.md

@@ -1 +0,0 @@
-# Elixir-cowboy

+ 0 - 24
frameworks/Elixir/cowboy/benchmark_config.json

@@ -1,24 +0,0 @@
-{
-    "framework": "elixir-cowboy",
-    "tests": [{
-        "default": {
-            "json_url": "/json",
-            "plaintext_url": "/plaintext",
-            "port": 8080,
-            "approach": "Stripped",
-            "classification": "Platform",
-            "database": "None",
-            "framework": "None",
-            "language": "Elixir",
-            "flavor": "None",
-            "orm": "raw",
-            "platform": "Cowboy",
-            "webserver": "None",
-            "os": "Linux",
-            "database_os": "Linux",
-            "display_name": "Cowboy",
-            "notes": "",
-            "versus": ""
-        }
-    }]
-}

+ 0 - 14
frameworks/Elixir/cowboy/config/config.exs

@@ -1,14 +0,0 @@
-use Mix.Config
-
-config :hello, http_ip: {0, 0, 0, 0}
-config :hello, http_port: 8080
-config :hello, compress_body: true
-config :hello, num_acceptors: 128
-config :hello, max_connections: 32000
-config :hello, max_keepalive: 1024
-config :logger,
-  level: :info,
-  compile_time_purge_matching: [
-    [level_lower_than: :info]
-  ]
-

+ 0 - 12
frameworks/Elixir/cowboy/elixir-cowboy.dockerfile

@@ -1,12 +0,0 @@
-FROM elixir:1.7.4
-
-ADD ./ /cowboy
-WORKDIR /cowboy
-
-ENV MIX_ENV=prod
-RUN mix local.hex --force
-RUN mix local.rebar --force
-RUN mix deps.get --force --only prod
-RUN mix compile --force
-
-CMD ["elixir", "--erl", "+K true +sbwt very_long +swt very_low", "--no-halt", "-S", "mix"]

+ 0 - 2
frameworks/Elixir/cowboy/lib/hello.ex

@@ -1,2 +0,0 @@
-defmodule Hello do
-end

+ 0 - 39
frameworks/Elixir/cowboy/lib/hello/application.ex

@@ -1,39 +0,0 @@
-defmodule Hello.Application do
-  @moduledoc false
-
-  use Application
-  require Logger
-
-  def start(_type, _args) do
-    _log_level = Application.get_env(:logger, :level)
-    http_ip = Application.get_env(:hello, :http_ip)
-    http_port = Application.get_env(:hello, :http_port)
-    compress_body = Application.get_env(:hello, :compress_body)
-    num_acceptors = Application.get_env(:hello, :num_acceptors)
-    max_connections = Application.get_env(:hello, :max_connections)
-    max_keepalive = Application.get_env(:hello, :max_keepalive)
-
-    children = [
-      {Plug.Cowboy,
-       scheme: :http,
-       plug: Hello.Router,
-       options: [
-         ip: http_ip,
-         port: http_port,
-         compress: compress_body,
-         transport_options: [
-           num_acceptors: num_acceptors,
-           max_connections: max_connections
-         ],
-         protocol_options: [
-           max_keepalive: max_keepalive
-         ]
-       ]}
-    ]
-
-    opts = [strategy: :one_for_one, name: Hello.Supervisor]
-    ip_str = http_ip |> Tuple.to_list() |> Enum.join(".")
-    Logger.info("Starting up on #{ip_str}:#{http_port}")
-    Supervisor.start_link(children, opts)
-  end
-end

+ 0 - 26
frameworks/Elixir/cowboy/lib/hello/router.ex

@@ -1,26 +0,0 @@
-defmodule Hello.Router do
-  use Plug.Router
-
-  plug(:match)
-  plug(:dispatch)
-
-  get "/" do
-    conn
-    |> put_resp_content_type("text/html")
-    |> send_resp(200, "<h2>/</h2>")
-  end
-
-  get "/json" do
-    conn
-    |> put_resp_content_type("application/json")
-    |> send_resp(200, Poison.encode!(%{message: "Hello, World!"}))
-  end
-
-  get "/plaintext" do
-    conn
-    |> put_resp_content_type("text/plain")
-    |> send_resp(200, "Hello, World!")
-  end
-
-  match(_, do: send_resp(conn, 404, "Oops!"))
-end

+ 0 - 30
frameworks/Elixir/cowboy/mix.exs

@@ -1,30 +0,0 @@
-defmodule Hello.MixProject do
-  use Mix.Project
-
-  def project do
-    [
-      app: :hello,
-      version: "0.1.0",
-      elixir: "~> 1.7",
-      start_permanent: Mix.env() == :prod,
-      deps: deps()
-    ]
-  end
-
-  def application do
-    [
-      extra_applications: [
-        :logger
-      ],
-      mod: {Hello.Application, []}
-    ]
-  end
-
-  defp deps do
-    [
-      {:plug_cowboy, "~> 2.0.1"},
-      {:poison, "~> 3.1"},
-      {:distillery, "~> 2.0"}
-    ]
-  end
-end

+ 0 - 55
frameworks/Elixir/cowboy/rel/config.exs

@@ -1,55 +0,0 @@
-# Import all plugins from `rel/plugins`
-# They can then be used by adding `plugin MyPlugin` to
-# either an environment, or release definition, where
-# `MyPlugin` is the name of the plugin module.
-~w(rel plugins *.exs)
-|> Path.join()
-|> Path.wildcard()
-|> Enum.map(&Code.eval_file(&1))
-
-use Mix.Releases.Config,
-    # This sets the default release built by `mix release`
-    default_release: :default,
-    # This sets the default environment used by `mix release`
-    default_environment: Mix.env()
-
-# For a full list of config options for both releases
-# and environments, visit https://hexdocs.pm/distillery/config/distillery.html
-
-
-# You may define one or more environments in this file,
-# an environment's settings will override those of a release
-# when building in that environment, this combination of release
-# and environment configuration is called a profile
-
-environment :dev do
-  # If you are running Phoenix, you should make sure that
-  # server: true is set and the code reloader is disabled,
-  # even in dev mode.
-  # It is recommended that you build with MIX_ENV=prod and pass
-  # the --env flag to Distillery explicitly if you want to use
-  # dev mode.
-  set dev_mode: true
-  set include_erts: false
-  set cookie: :"oHiKgSD18Or5NI|AQIwET6;ni=}i]aq:>_2V/|)4A:*73@(&sB>q^CXj*,n/7hcm"
-end
-
-environment :prod do
-  set include_erts: true
-  set include_src: false
-  set cookie: :"U0zRt1a7BRIi5(<Dg@jQ;:o>Vr=Ezcr``J&SO`v:TPrQMo/PWV;<^IVzU^Y~(Jvy"
-  set vm_args: "rel/vm.args"
-end
-
-# You may define one or more releases in this file.
-# If you have not set a default release, or selected one
-# when running `mix release`, the first release in the file
-# will be used by default
-
-release :hello do
-  set version: current_version(:hello)
-  set applications: [
-    :runtime_tools
-  ]
-end
-

+ 0 - 3
frameworks/Elixir/cowboy/rel/plugins/.gitignore

@@ -1,3 +0,0 @@
-*.*
-!*.exs
-!.gitignore

+ 0 - 30
frameworks/Elixir/cowboy/rel/vm.args

@@ -1,30 +0,0 @@
-## This file provide the arguments provided to the VM at startup
-## You can find a full list of flags and their behaviours at
-## http://erlang.org/doc/man/erl.html
-
-## Name of the node
--name <%= release_name %>@127.0.0.1
-
-## Cookie for distributed erlang
--setcookie <%= release.profile.cookie %>
-
-## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
-## (Disabled by default..use with caution!)
-##-heart
-
-## Enable kernel poll and a few async threads
-+K true
-##+A 5
-## For OTP21+, the +A flag is not used anymore,
-## +SDio replace it to use dirty schedulers
-+SDio 5
-
-## Increase number of concurrent ports/sockets
-##-env ERL_MAX_PORTS 4096
-
-## Tweak GC to run more often
-##-env ERL_FULLSWEEP_AFTER 10
-
-# Enable SMP automatically based on availability
-# On OTP21+, this is not needed anymore.
--smp auto

+ 0 - 15
frameworks/Elixir/cowboy/setup.sh

@@ -1,15 +0,0 @@
-#!/bin/bash
-
-fw_depends elixir
-
-rm -rf _build deps
-
-export MIX_ENV=prod
-mix local.hex --force
-mix local.rebar --force
-mix deps.get --force --only prod
-mix compile --force
-mix release
-_build/prod/rel/hello/bin/hello foreground
-
-#elixir --erl "+K true +sbwt very_long +swt very_low" --detached --no-halt -S mix

+ 4 - 0
frameworks/Elixir/plug/.formatter.exs

@@ -0,0 +1,4 @@
+# Used by "mix format"
+[
+  inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
+]

+ 26 - 0
frameworks/Elixir/plug/.gitignore

@@ -0,0 +1,26 @@
+# The directory Mix will write compiled artifacts to.
+/_build/
+
+# If you run "mix test --cover", coverage assets end up here.
+/cover/
+
+# The directory Mix downloads your dependencies sources to.
+/deps/
+
+# Where third-party dependencies like ExDoc output generated docs.
+/doc/
+
+# Ignore .fetch files in case you like to edit your project deps locally.
+/.fetch
+
+# If the VM crashes, it generates a dump, let's ignore it too.
+erl_crash.dump
+
+# Also ignore archive artifacts (built via "mix archive.build").
+*.ez
+
+# Ignore package tarball (built via "mix hex.build").
+framework_benchmarks-*.tar
+
+#Include mix.lock that was excluded at the root as *.lock
+!mix.lock

+ 50 - 0
frameworks/Elixir/plug/README.md

@@ -0,0 +1,50 @@
+# Elixir Plug Benchmarking Test
+
+### Test Type Implementation Source Code
+
+* [JSON](lib/framework_benchmarks/handlers/json.ex)
+* [PLAINTEXT](lib/framework_benchmarks/handlers/plain-text.ex)
+* [DB](lib/framework_benchmarks/handlers/db.ex)
+* [QUERY](lib/framework_benchmarks/handlers/query.ex)
+* [CACHED QUERY](lib/framework_benchmarks/handlers/cached-world.ex)
+* [UPDATE](lib/framework_benchmarks/handlers/update.ex)
+* [FORTUNES](lib/framework_benchmarks/handlers/fortune.ex)
+
+## Important Libraries
+The tests were run with:
+* [Plug Cowboy - Webserver](https://github.com/elixir-plug/plug_cowboy)
+* [ElJiffy - JSON encoder](https://github.com/lilrooness/eljiffy)
+* [Ecto SQL - Fetching and Updating DB](https://github.com/elixir-ecto/ecto_sql)
+* [Postgrex - Postgres SQL Adapter](https://github.com/elixir-ecto/postgrex)
+* [Cachex - Cache](https://github.com/whitfin/cachex)
+* [Phoenix HTML - HTML encoding](https://github.com/phoenixframework/phoenix_html)
+* [ucol - Unicode ICU based collation](https://github.com/barrel-db/erlang-ucol)
+
+## Test URLs
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/query?queries=
+
+### CACHED QUERY
+
+http://localhost:8080/cached-worlds?count=
+
+### UPDATE
+
+http://localhost:8080/update?queries=
+
+### FORTUNES
+
+http://localhost:8080/fortunes

+ 32 - 0
frameworks/Elixir/plug/benchmark_config.json

@@ -0,0 +1,32 @@
+{
+  "framework": "elixir-plug-ecto",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "cached_query_url": "/cached-worlds?count=",
+        "update_url": "/updates?queries=",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "cache": "Cachex",
+        "database": "postgres",
+        "framework": "Plug",
+        "language": "Elixir",
+        "flavor": "None",
+        "orm": "Full",
+        "platform": "BEAM",
+        "webserver": "cowboy",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Elixir Plug Ecto",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 5 - 0
frameworks/Elixir/plug/config/config.exs

@@ -0,0 +1,5 @@
+use Mix.Config
+
+config :framework_benchmarks, ecto_repos: [FrameworkBenchmarks.Repo]
+
+import_config "#{Mix.env()}.exs"

+ 1 - 0
frameworks/Elixir/plug/config/dev.exs

@@ -0,0 +1 @@
+import Config

+ 1 - 0
frameworks/Elixir/plug/config/prod.exs

@@ -0,0 +1 @@
+import Config

+ 14 - 0
frameworks/Elixir/plug/config/releases.exs

@@ -0,0 +1,14 @@
+import Config
+
+config :logger, level: :error
+
+config :framework_benchmarks, FrameworkBenchmarks.Repo,
+  username: "benchmarkdbuser",
+  password: "benchmarkdbpass",
+  database: "hello_world",
+  hostname: "tfb-database",
+  port: 5432,
+  pool_size: 300,
+  queue_target: 5000,
+  queue_interval: 5000,
+  log: false

+ 19 - 0
frameworks/Elixir/plug/docker-compose.yml

@@ -0,0 +1,19 @@
+version: '2.3'
+
+services:
+  app:
+    build:
+      context: .
+      dockerfile: elixir-plug-ecto.dockerfile
+      target: app
+    ports:
+      - "8080:8080"
+  nginx:
+    image: jwilder/nginx-proxy:alpine
+    environment:
+      VIRTUAL_HOST: 0.0.0.0
+      VIRTUAL_PORT: 5000
+    volumes:
+      - "/var/run/docker.sock:/tmp/docker.sock:ro"
+    ports:
+      - "4000:80"

+ 37 - 0
frameworks/Elixir/plug/elixir-plug-ecto.dockerfile

@@ -0,0 +1,37 @@
+FROM elixir:1.9.4 as builder
+
+RUN apt update -y && \
+  apt install -y libicu-dev
+
+ENV MIX_ENV=prod \
+  LANG=C.UTF-8
+
+RUN mkdir /app
+WORKDIR /app
+
+COPY config ./config
+COPY lib ./lib
+COPY mix.exs .
+COPY mix.lock .
+
+RUN mix local.hex --force
+RUN mix local.rebar --force
+RUN mix deps.get
+RUN mix deps.compile
+RUN mix release
+
+FROM debian:buster-slim AS app
+
+RUN apt update -y && \
+  apt install -y openssl libicu-dev
+
+ENV LANG=C.UTF-8
+
+EXPOSE 8080
+
+RUN adduser -h /home/app -D app
+WORKDIR /home/app
+COPY --from=builder /app/_build .
+
+# Run the Phoenix app
+CMD ["./prod/rel/framework_benchmarks/bin/framework_benchmarks", "start"]

+ 31 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/application.ex

@@ -0,0 +1,31 @@
+defmodule FrameworkBenchmarks.Application do
+  # See https://hexdocs.pm/elixir/Application.html
+  # for more information on OTP Applications
+  @moduledoc false
+
+  use Application
+
+  def start(_type, _args) do
+    children = [
+      {Registry, keys: :unique, name: FrameworkBenchmarks.Registry},
+      FrameworkBenchmarks.Repo,
+      FrameworkBenchmarks.CachedWorld,
+      {Plug.Cowboy,
+       scheme: :http,
+       plug: FrameworkBenchmarks.Endpoints,
+       options: [
+         port: 8080,
+         protocol_options: [
+           max_keepalive: 32768
+         ],
+         transport_options: [
+           max_connections: :infinity,
+           num_acceptors: 32768
+         ]
+       ]}
+    ]
+
+    opts = [strategy: :one_for_one, name: FrameworkBenchmarks.Supervisor]
+    Supervisor.start_link(children, opts)
+  end
+end

+ 51 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/cached_world.ex

@@ -0,0 +1,51 @@
+defmodule FrameworkBenchmarks.CachedWorld do
+  @moduledoc """
+  Cache for the CachedWorld table
+  """
+
+  use GenServer
+
+  def start_link(_) do
+    GenServer.start_link(__MODULE__, :ok,
+      name: {:via, Registry, {FrameworkBenchmarks.Registry, __MODULE__}}
+    )
+  end
+
+  def get(id) do
+    GenServer.call({:via, Registry, {FrameworkBenchmarks.Registry, __MODULE__}}, {:get, id})
+  end
+
+  @impl true
+  def init(_) do
+    Cachex.start_link(:cached_world, [])
+
+    records =
+      FrameworkBenchmarks.Repo.all(FrameworkBenchmarks.Models.World)
+      |> Enum.map(fn record ->
+        {
+          Map.get(record, :id),
+          record
+          |> Map.from_struct()
+          |> Map.drop([:__meta__])
+        }
+      end)
+
+    Cachex.put_many(:cached_world, records)
+  end
+
+  @impl true
+  def handle_call({:get, id}, _from, state) do
+    record =
+      case Cachex.get(:cached_world, id) do
+        {:ok, nil} ->
+          FrameworkBenchmarks.Repo.get(FrameworkBenchmarks.Models.World, id)
+          |> Map.from_struct()
+          |> Map.drop([:__meta__])
+
+        {:ok, value} ->
+          value
+      end
+
+    {:reply, record, state}
+  end
+end

+ 43 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/endpoints.ex

@@ -0,0 +1,43 @@
+defmodule FrameworkBenchmarks.Endpoints do
+  @moduledoc """
+  The plug endpoints for the benchmarks
+  """
+  use Plug.Router
+
+  alias FrameworkBenchmarks.Handlers.{JSON, DB, Query, CachedWorld, PlainText, Fortune, Update}
+
+  plug(:match)
+  plug(:dispatch)
+
+  get "/json" do
+    JSON.handle(conn)
+  end
+
+  get "/db" do
+    DB.handle(conn)
+  end
+
+  get "/queries" do
+    Query.handle(conn)
+  end
+
+  get "/cached-worlds" do
+    CachedWorld.handle(conn)
+  end
+
+  get "/plaintext" do
+    PlainText.handle(conn)
+  end
+
+  get "/fortunes" do
+    Fortune.handle(conn)
+  end
+
+  get "/updates" do
+    Update.handle(conn)
+  end
+
+  match _ do
+    send_resp(conn, 404, "<h1>Page Not Found</h1>")
+  end
+end

+ 23 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/handlers/cached-world.ex

@@ -0,0 +1,23 @@
+defmodule FrameworkBenchmarks.Handlers.CachedWorld do
+  @moduledoc """
+  handler for the /cached-worlds route
+  """
+  def handle(conn) do
+    number_of_queries = FrameworkBenchmarks.Handlers.Helpers.parse_queries(conn, "count")
+
+    ids =
+      1..number_of_queries
+      |> Enum.map(fn _ ->
+        :rand.uniform(10_000)
+      end)
+
+    {:ok, json} =
+      ids
+      |> Enum.map(&FrameworkBenchmarks.CachedWorld.get/1)
+      |> Eljiffy.encode()
+
+    conn
+    |> Plug.Conn.put_resp_content_type("application/json")
+    |> Plug.Conn.send_resp(200, json)
+  end
+end

+ 18 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/handlers/db.ex

@@ -0,0 +1,18 @@
+defmodule FrameworkBenchmarks.Handlers.DB do
+  @moduledoc """
+  This is the handler for the /db route
+  """
+  def handle(conn) do
+    id = :rand.uniform(10_000)
+
+    {:ok, json} =
+      FrameworkBenchmarks.Repo.get(FrameworkBenchmarks.Models.World, id)
+      |> Map.from_struct()
+      |> Map.drop([:__meta__])
+      |> Eljiffy.encode()
+
+    conn
+    |> Plug.Conn.put_resp_content_type("application/json")
+    |> Plug.Conn.send_resp(200, json)
+  end
+end

+ 48 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/handlers/fortune.ex

@@ -0,0 +1,48 @@
+defmodule FrameworkBenchmarks.Handlers.Fortune do
+  @moduledoc """
+  This is the handle for the /fortunes route
+  """
+
+  require EEx
+
+  @fortune_template """
+  <!DOCTYPE html>
+  <html>
+  <head><title>Fortunes</title></head>
+  <body>
+    <table>
+      <tr><th>id</th><th>message</th></tr>
+      <%= for item <- items do %>
+      <tr><td><%= item.id %></td><td><%= item.message %></td></tr>
+      <% end %>
+    </table>
+  </body>
+  </html>
+  """
+
+  EEx.function_from_string(:defp, :generate_fortunes, @fortune_template, [:items])
+
+  @new_message "Additional fortune added at request time."
+
+  def handle(conn) do
+    fortunes =
+      FrameworkBenchmarks.Repo.all(FrameworkBenchmarks.Models.Fortune)
+      |> Enum.map(fn fortune ->
+        safe_result = Phoenix.HTML.html_escape(fortune.message)
+        %{id: fortune.id, message: Phoenix.HTML.safe_to_string(safe_result)}
+      end)
+
+    fortunes = fortunes ++ [%{id: 0, message: @new_message}]
+
+    fortunes =
+      fortunes
+      |> Enum.sort(fn %{message: first}, %{message: second}
+                      when is_binary(first) and is_binary(second) ->
+        :ucol.compare(first, second) != 1
+      end)
+
+    conn
+    |> Plug.Conn.put_resp_content_type("text/html")
+    |> Plug.Conn.send_resp(200, generate_fortunes(fortunes))
+  end
+end

+ 21 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/handlers/helpers.ex

@@ -0,0 +1,21 @@
+defmodule FrameworkBenchmarks.Handlers.Helpers do
+  @moduledoc """
+  Helper functions for shared tasks between routes
+  """
+
+  @doc """
+  Used to parse the number value from the query string between 1 and 500
+  """
+  def parse_queries(conn, name) do
+    with %{^name => count} <- Plug.Conn.fetch_query_params(conn).query_params do
+      case Integer.parse(count) do
+        {num, _} when num >= 1 and num <= 500 -> num
+        {num, _} when num < 1 -> 1
+        {num, _} when num > 500 -> 500
+        _ -> 1
+      end
+    else
+      _ -> 1
+    end
+  end
+end

+ 12 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/handlers/json.ex

@@ -0,0 +1,12 @@
+defmodule FrameworkBenchmarks.Handlers.JSON do
+  @moduledoc """
+  This is the handle for the /json route
+  """
+  def handle(conn) do
+    {:ok, json} = Eljiffy.encode(%{message: "Hello, World!"})
+
+    conn
+    |> Plug.Conn.put_resp_content_type("application/json")
+    |> Plug.Conn.send_resp(200, json)
+  end
+end

+ 10 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/handlers/plain-text.ex

@@ -0,0 +1,10 @@
+defmodule FrameworkBenchmarks.Handlers.PlainText do
+  @moduledoc """
+  This is the handle for the /plaintext route
+  """
+  def handle(conn) do
+    conn
+    |> Plug.Conn.put_resp_content_type("text/plain")
+    |> Plug.Conn.send_resp(200, "Hello, World!")
+  end
+end

+ 33 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/handlers/query.ex

@@ -0,0 +1,33 @@
+defmodule FrameworkBenchmarks.Handlers.Query do
+  @moduledoc """
+  handler for the /queries route
+  """
+  def handle(conn) do
+    number_of_queries = FrameworkBenchmarks.Handlers.Helpers.parse_queries(conn, "queries")
+
+    ids =
+      1..number_of_queries
+      |> Enum.map(fn _ ->
+        :rand.uniform(10_000)
+      end)
+
+    records =
+      ids
+      |> Enum.map(fn id ->
+        FrameworkBenchmarks.Repo.get(FrameworkBenchmarks.Models.World, id)
+      end)
+
+    {:ok, json} =
+      records
+      |> Enum.map(fn record ->
+        record
+        |> Map.from_struct()
+        |> Map.drop([:__meta__])
+      end)
+      |> Eljiffy.encode()
+
+    conn
+    |> Plug.Conn.put_resp_content_type("application/json")
+    |> Plug.Conn.send_resp(200, json)
+  end
+end

+ 56 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/handlers/update.ex

@@ -0,0 +1,56 @@
+defmodule FrameworkBenchmarks.Handlers.Update do
+  @moduledoc """
+  handler for the /updates route
+  """
+
+  defp random_but(not_this_value) do
+    case :rand.uniform(10_000) do
+      new_value when new_value == not_this_value ->
+        random_but(not_this_value)
+
+      new_value ->
+        new_value
+    end
+  end
+
+  def handle(conn) do
+    number_of_queries = FrameworkBenchmarks.Handlers.Helpers.parse_queries(conn, "queries")
+
+    ids =
+      1..number_of_queries
+      |> Enum.map(fn _ ->
+        :rand.uniform(10_000)
+      end)
+
+    records =
+      ids
+      |> Enum.map(fn id ->
+        FrameworkBenchmarks.Repo.get(FrameworkBenchmarks.Models.World, id)
+      end)
+      |> Enum.map(fn %{randomnumber: current_randomnumber} = world ->
+        {:ok, changed_world} =
+          Ecto.Changeset.change(
+            world,
+            %{
+              randomnumber: random_but(current_randomnumber)
+            }
+          )
+          |> FrameworkBenchmarks.Repo.update()
+
+        changed_world
+      end)
+
+    {:ok, json} =
+      records
+      |> Enum.map(fn record ->
+        record
+        |> Map.from_struct()
+        |> Map.drop([:__meta__])
+      end)
+      |> Eljiffy.encode()
+
+    conn
+    |> Plug.Conn.put_resp_content_type("application/json")
+    |> Plug.Conn.send_resp(200, json)
+  end
+end

+ 7 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/models/fortune.ex

@@ -0,0 +1,7 @@
+defmodule FrameworkBenchmarks.Models.Fortune do
+  use Ecto.Schema
+
+  schema "fortune" do
+    field(:message, :binary)
+  end
+end

+ 7 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/models/world.ex

@@ -0,0 +1,7 @@
+defmodule FrameworkBenchmarks.Models.World do
+  use Ecto.Schema
+
+  schema "world" do
+    field(:randomnumber, :integer)
+  end
+end

+ 5 - 0
frameworks/Elixir/plug/lib/framework_benchmarks/repo.ex

@@ -0,0 +1,5 @@
+defmodule FrameworkBenchmarks.Repo do
+  use Ecto.Repo,
+    otp_app: :framework_benchmarks,
+    adapter: Ecto.Adapters.Postgres
+end

+ 34 - 0
frameworks/Elixir/plug/mix.exs

@@ -0,0 +1,34 @@
+defmodule FrameworkBenchmarks.MixProject do
+  use Mix.Project
+
+  def project do
+    [
+      app: :framework_benchmarks,
+      version: "0.1.0",
+      elixir: "~> 1.9",
+      start_permanent: Mix.env() == :prod,
+      deps: deps()
+    ]
+  end
+
+  # Run "mix help compile.app" to learn about applications.
+  def application do
+    [
+      extra_applications: [:cachex, :logger],
+      mod: {FrameworkBenchmarks.Application, []}
+    ]
+  end
+
+  # Run "mix help deps" to learn about dependencies.
+  defp deps do
+    [
+      {:plug_cowboy, "~> 2.0"},
+      {:eljiffy, "~> 1.3.0"},
+      {:ecto_sql, "~> 3.0"},
+      {:postgrex, ">= 0.0.0"},
+      {:cachex, "~> 3.2"},
+      {:phoenix_html, "~> 2.13"},
+      {:ucol, "~> 2.0"}
+    ]
+  end
+end

+ 25 - 0
frameworks/Elixir/plug/mix.lock

@@ -0,0 +1,25 @@
+%{
+  "cachex": {:hex, :cachex, "3.2.0", "a596476c781b0646e6cb5cd9751af2e2974c3e0d5498a8cab71807618b74fe2f", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
+  "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
+  "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
+  "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm"},
+  "db_connection": {:hex, :db_connection, "2.2.0", "e923e88887cd60f9891fd324ac5e0290954511d090553c415fbf54be4c57ee63", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
+  "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm"},
+  "ecto": {:hex, :ecto, "3.3.1", "82ab74298065bf0c64ca299f6c6785e68ea5d6b980883ee80b044499df35aba1", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
+  "ecto_sql": {:hex, :ecto_sql, "3.3.2", "92804e0de69bb63e621273c3492252cb08a29475c05d40eeb6f41ad2d483cfd3", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
+  "eljiffy": {:hex, :eljiffy, "1.3.0", "7e584be454c5ec3fc3ae472eedb4cb2185e9ed6cd863df383ef601de3f3b27fd", [:mix], [{:jiffy, "~> 1.0", [hex: :jiffy, repo: "hexpm", optional: false]}], "hexpm"},
+  "eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
+  "jiffy": {:hex, :jiffy, "1.0.1", "4f25639772ca41202f41ba9c8f6ca0933554283dd4742c90651e03471c55e341", [:rebar3], [], "hexpm"},
+  "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm"},
+  "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
+  "phoenix_html": {:hex, :phoenix_html, "2.13.3", "850e292ff6e204257f5f9c4c54a8cb1f6fbc16ed53d360c2b780a3d0ba333867", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
+  "plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
+  "plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
+  "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
+  "postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
+  "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
+  "sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm"},
+  "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm"},
+  "ucol": {:hex, :ucol, "2.0.0", "64f9589d682dac6ca59252e1222e22697784f74addd0b88c5e34d53d267356bb", [:rebar3], [], "hexpm"},
+  "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm"},
+}

+ 8 - 0
frameworks/Elixir/plug/test/framework_benchmarks_test.exs

@@ -0,0 +1,8 @@
+defmodule FrameworkBenchmarksTest do
+  use ExUnit.Case
+  doctest FrameworkBenchmarks
+
+  test "greets the world" do
+    assert FrameworkBenchmarks.hello() == :world
+  end
+end

+ 1 - 0
frameworks/Elixir/plug/test/test_helper.exs

@@ -0,0 +1 @@
+ExUnit.start()