Browse Source

Adds crystal grip framework (#5643)

* adds grip framework

* adds parallel support to the benchmark

Co-authored-by: Carlos Donderis <[email protected]>
Carlos Donderis 5 years ago
parent
commit
39837386d1

+ 51 - 0
frameworks/Crystal/grip/README.md

@@ -0,0 +1,51 @@
+This is the [Grip](https://github.com/grip-framework/grip) test of the Framework Benchmarks.
+Crystal is a new language that closely resembles Ruby with a goal of removing typed variables and parameters (instead inferencing), whilst maintaining top speed through bindings into C.
+
+Grip is a microframework for building RESTful web applications, with ease and joy.
+
+
+# Grip Benchmarking Test
+
+### Test Type Implementation Source Code
+
+* [JSON](grip.cr)
+* [PLAINTEXT](grip.cr)
+* [DB](grip.cr)
+* [QUERY](grip.cr)
+* [CACHED QUERY](grip.cr)
+* [UPDATE](grip.cr)
+* [FORTUNES](grip.cr)
+
+## Important Libraries
+The tests were run with:
+* [Software](https://www.example1.com/)
+* [Example](http://www.example2.com/)
+
+## 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_query?queries=
+
+### UPDATE
+
+http://localhost:8080/update?queries=
+
+### FORTUNES
+
+http://localhost:8080/fortunes

+ 30 - 0
frameworks/Crystal/grip/benchmark_config.json

@@ -0,0 +1,30 @@
+{
+  "framework": "grip",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "plaintext_url": "/plaintext",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "postgres",
+        "framework": "Grip",
+        "language": "Crystal",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Grip",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 149 - 0
frameworks/Crystal/grip/grip.cr

@@ -0,0 +1,149 @@
+require "grip"
+require "pg"
+require "ecr/macros"
+
+APPDB      = DB.open(ENV["DATABASE_URL"])
+ID_MAXIMUM = 10_000
+
+module Grip
+  module DSL
+    module Methods
+       def html(context, content, status_code = HTTP::Status::OK)
+        context.response.status_code = status_code.to_i
+        context.response.headers.merge!({"Content-Type" => "text/html; charset=UTF-8"})
+        context.response.print(content)
+        context.response
+      end
+    end
+  end
+end
+
+private def random_world
+  id = rand(1..ID_MAXIMUM)
+  id, random_number = APPDB.query_one("SELECT id, randomNumber FROM world WHERE id = $1", id, as: {Int32, Int32})
+  {id: id, randomNumber: random_number}
+end
+
+private def set_world(world)
+  APPDB.exec("UPDATE world SET randomNumber = $1 WHERE id = $2", world[:randomNumber], world[:id])
+  world
+end
+
+private def fortunes
+  data = Array(NamedTuple(id: Int32, message: String)).new
+
+  APPDB.query_each("SELECT id, message FROM Fortune") do |rs|
+    data.push({id: rs.read(Int32), message: rs.read(String)})
+  end
+
+  data
+end
+
+private def sanitized_query_count(request)
+  queries = request.query_params["queries"]? || "1"
+  queries = queries.to_i? || 1
+  queries.clamp(1..500)
+end
+
+class Json < Grip::Controller::Http
+  def get(context)
+    context.response.headers["Server"] = "Grip"
+    context.response.headers["Date"] = HTTP.format_time(Time.utc)
+    json(
+      context,
+      {
+        message: "Hello, World!",
+      }
+    )
+  end
+end
+
+class Plaintext < Grip::Controller::Http
+  def get(context)
+    context.response.headers["Server"] = "Grip"
+    context.response.headers["Date"] = HTTP.format_time(Time.utc)
+    text(
+      context,
+      "Hello, World!"
+    )
+  end
+end
+
+class Db < Grip::Controller::Http
+  def get(context)
+    context.response.headers["Server"] = "Grip"
+    context.response.headers["Date"] = HTTP.format_time(Time.utc)
+    json(
+      context,
+      random_world
+    )
+  end
+end
+
+class Queries < Grip::Controller::Http
+  def get(context)
+    results = (1..sanitized_query_count(context.request)).map do
+      random_world
+    end
+
+    context.response.headers["Server"] = "Grip"
+    context.response.headers["Date"] = HTTP.format_time(Time.utc)
+    json(
+      context,
+      results
+    )
+  end
+end
+
+class Updates < Grip::Controller::Http
+  def get(context)
+    updated = (1..sanitized_query_count(context.request)).map do
+      set_world({id: random_world[:id], randomNumber: rand(1..ID_MAXIMUM)})
+    end
+    context.response.headers["Server"] = "Grip"
+    context.response.headers["Date"] = HTTP.format_time(Time.utc)
+    json(
+      context,
+      updated
+    )
+  end
+end
+
+class Fortunes < Grip::Controller::Http
+  def get(context)
+    data = fortunes
+    additional_fortune = {
+      id:      0,
+      message: "Additional fortune added at request time.",
+    }
+    data.push(additional_fortune)
+    data.sort_by! { |fortune| fortune[:message] }
+
+    context.response.headers["Server"] = "Grip"
+    context.response.headers["Date"] = HTTP.format_time(Time.utc)
+
+    io = IO::Memory.new
+    ECR.embed "views/fortunes.ecr", io
+    html(
+      context,
+      io.to_s
+    )
+  end
+end
+
+class Application < Grip::Application
+  def initialize
+    get "/json", Json
+    get "/plaintext", Plaintext
+    get "/db", Db
+    get "/queries", Queries
+    get "/updates", Updates
+    get "/fortunes", Fortunes
+  end
+end
+
+app = Application.new
+app.run(8080) do |config|
+  server = config.server.not_nil!
+  server.bind_tcp "0.0.0.0", 8080, reuse_port: true
+end

+ 15 - 0
frameworks/Crystal/grip/grip.dockerfile

@@ -0,0 +1,15 @@
+FROM crystallang/crystal:0.34.0
+
+WORKDIR /grip
+COPY views views
+COPY run.sh run.sh
+COPY grip.cr grip.cr
+COPY shard.yml shard.yml
+
+ENV DATABASE_URL postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world?initial_pool_size=56&max_idle_pool_size=56
+ENV GRIP_ENV production
+
+RUN shards install
+RUN crystal build  --release --no-debug grip.cr
+
+CMD bash run.sh

+ 7 - 0
frameworks/Crystal/grip/run.sh

@@ -0,0 +1,7 @@
+#!/bin/bash
+
+for i in $(seq 1 $(nproc --all)); do
+  ./grip -p 8080 &
+done
+
+wait

+ 14 - 0
frameworks/Crystal/grip/shard.yml

@@ -0,0 +1,14 @@
+name: hoge
+version: 0.1.0
+
+dependencies:
+  grip:
+    github: grip-framework/grip
+  pg:
+    github: will/crystal-pg
+    version: 0.20.0
+targets:
+  grip:
+    main: grip.cr
+
+crystal: 0.34.0

+ 20 - 0
frameworks/Crystal/grip/views/fortunes.ecr

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>Fortunes</title>
+</head>
+<body>
+	<table>
+		<tr>
+		<th>id</th>
+		<th>message</th>
+		</tr>
+		<% data.each do |fortune| %>
+			<tr>
+			<td><%= fortune[:id] %></td>
+			<td><%= HTML.escape(fortune[:message]) %></td>
+			</tr>
+		<% end %>
+	</table>
+</body>
+</html>