Browse Source

add spider-gazelle framework (#4172)

* add spider-gazelle framework

* simplify dockerfile

* remove local license
Stephen von Takach 6 years ago
parent
commit
27a07431f4

+ 7 - 0
frameworks/Crystal/spider-gazelle/README.md

@@ -0,0 +1,7 @@
+# TechEmpower on Spider-Gazelle
+
+Spider-Gazelle is written in [Crystal](http://www.crystal-lang.org) designed for performance, without compromising developer productivity / happiness.
+
+Detailed documentation and guides available: https://spider-gazelle.net/
+
+This project uses the MIT License

+ 28 - 0
frameworks/Crystal/spider-gazelle/benchmark_config.json

@@ -0,0 +1,28 @@
+{
+  "framework": "spider-gazelle",
+  "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": "Fullstack",
+      "database": "Postgres",
+      "framework": "Spider-Gazelle",
+      "language": "Crystal",
+      "flavor": "None",
+      "orm": "Full",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Spider-Gazelle (MVC, PSQL)",
+      "notes": "",
+      "versus": "amber"
+    }
+  }]
+}

+ 30 - 0
frameworks/Crystal/spider-gazelle/shard.yml

@@ -0,0 +1,30 @@
+name: app
+version: 1.0.0
+
+dependencies:
+  action-controller:
+    github: spider-gazelle/action-controller
+    version: "~> 1.1"
+
+  granite:
+    github: amberframework/granite
+    version: "~> 0.15"
+
+  # https://github.com/jeromegn/kilt
+  # Generic template interface for Crystal
+  kilt:
+    github: jeromegn/kilt
+    version: "~> 0.3"
+
+  pg:
+    github: will/crystal-pg
+    version: "~> 0.15"
+
+development_dependencies:
+  ameba:
+    github: veelenga/ameba
+
+# compile target
+targets:
+  app:
+    main: src/app.cr

+ 14 - 0
frameworks/Crystal/spider-gazelle/spider-gazelle.dockerfile

@@ -0,0 +1,14 @@
+FROM crystallang/crystal:0.27.0
+ADD . /src
+WORKDIR /src
+
+# Build App
+RUN shards build --production
+
+ENV DATABASE_URL postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world
+ENV SG_ENV production
+
+# Run the app binding on port 8080
+EXPOSE 8080
+ENTRYPOINT ["/src/bin/app"]
+CMD ["/src/bin/app", "-b", "0.0.0.0", "-p", "8080", "-w", "0"]

+ 58 - 0
frameworks/Crystal/spider-gazelle/src/app.cr

@@ -0,0 +1,58 @@
+require "option_parser"
+require "./config"
+
+# Server defaults
+port = 3000
+host = "127.0.0.1"
+cluster = false
+process_count = 1
+
+# Command line options
+OptionParser.parse(ARGV.dup) do |parser|
+  parser.banner = "Usage: #{PROGRAM_NAME} [arguments]"
+
+  parser.on("-b HOST", "--bind=HOST", "Specifies the server host") { |h| host = h }
+  parser.on("-p PORT", "--port=PORT", "Specifies the server port") { |p| port = p.to_i }
+
+  parser.on("-w COUNT", "--workers=COUNT", "Specifies the number of processes to handle requests") do |w|
+    cluster = true
+    process_count = w.to_i
+  end
+
+  parser.on("-r", "--routes", "List the application routes") do
+    ActionController::Server.print_routes
+    exit 0
+  end
+
+  parser.on("-v", "--version", "Display the application version") do
+    puts "#{APP_NAME} v#{VERSION}"
+    exit 0
+  end
+
+  parser.on("-h", "--help", "Show this help") do
+    puts parser
+    exit 0
+  end
+end
+
+# Load the routes
+puts "Launching #{APP_NAME} v#{VERSION}"
+server = ActionController::Server.new(port, host)
+
+# Start clustering
+server.cluster(process_count, "-w", "--workers") if cluster
+
+# Detect ctr-c to shutdown gracefully
+Signal::INT.trap do |signal|
+  puts " > terminating gracefully"
+  spawn { server.close }
+  signal.ignore
+end
+
+# Start the server
+server.run do
+  puts "Listening on #{server.print_addresses}"
+end
+
+# Shutdown message
+puts "#{APP_NAME} leaps through the veldt\n"

+ 30 - 0
frameworks/Crystal/spider-gazelle/src/config.cr

@@ -0,0 +1,30 @@
+# Application dependencies
+require "action-controller"
+
+require "granite/adapter/pg"
+Granite.settings.logger = Logger.new(nil)
+Granite::Adapters << Granite::Adapter::Pg.new({name: "pg", url: ENV["DATABASE_URL"]})
+
+# Application code
+require "./controllers/application"
+require "./controllers/*"
+require "./models/*"
+
+# Server required after application controllers
+require "action-controller/server"
+
+# Add handlers that should run before your application
+ActionController::Server.before(
+  HTTP::LogHandler.new(STDOUT),
+  HTTP::ErrorHandler.new(ENV["SG_ENV"]? != "production")
+)
+
+# Configure session cookies
+# NOTE:: Change these from defaults
+ActionController::Session.configure do
+  settings.key = ENV["COOKIE_SESSION_KEY"]? || "_spider_gazelle_"
+  settings.secret = ENV["COOKIE_SESSION_SECRET"]? || "4f74c0b358d5bab4000dd3c75465dc2c"
+end
+
+APP_NAME = "TechEmpower on SG"
+VERSION  = "1.0.0"

+ 11 - 0
frameworks/Crystal/spider-gazelle/src/controllers/application.cr

@@ -0,0 +1,11 @@
+# Require kilt for template support
+require "kilt"
+
+abstract class Application < ActionController::Base
+  before_action :set_date_header
+
+  def set_date_header
+    response.headers["Server"] = "Spider-Gazelle"
+    response.headers["Date"] = HTTP.format_time(Time.now)
+  end
+end

+ 72 - 0
frameworks/Crystal/spider-gazelle/src/controllers/benchmark.cr

@@ -0,0 +1,72 @@
+class Benchmark < Application
+  ID_MAXIMUM = 10_000
+
+  base "/"
+  before_action :get_query_count, only: [:queries, :updates]
+
+  def get_query_count
+    queries = query_params["queries"]
+    queries = queries.to_i? || 1
+    @queries = queries.clamp(1..500)
+  end
+  @queries : Int32 = 0
+
+  # Test 1: JSON Serialization
+  get "/json", :json do
+    render json: {message: "Hello, World!"}
+  end
+
+  # Test 6: Plaintext
+  get "/plaintext", :plaintext do
+    render text: "Hello, World!"
+  end
+
+  # Postgres Test 2: Single database query
+  get "/db", :db do
+    results = {} of Symbol => Int32
+    if world = World.find rand(1..ID_MAXIMUM)
+      results = {id: world.id, randomNumber: world.randomnumber}
+    end
+
+    render json: results
+  end
+
+  # Postgres Test 3: Multiple database query
+  get "/queries", :queries do
+    results = (1..@queries).map do
+      if world = World.find rand(1..ID_MAXIMUM)
+        {id: world.id, randomNumber: world.randomnumber}
+      end
+    end
+
+    render json: results
+  end
+
+  # Postgres Test 5: Database Updates
+  get "/updates", :updates do
+    results = (1..@queries).map do
+      if world = World.find rand(1..ID_MAXIMUM)
+        world.randomnumber = rand(1..ID_MAXIMUM)
+        world.save
+        {id: world.id, randomNumber: world.randomnumber}
+      end
+    end
+
+    render json: results
+  end
+
+  # Postgres Test 4: Fortunes
+  get "/fortunes", :fortunes do
+    fortune = Fortune.new
+    fortune.id = 0
+    fortune.message = "Additional fortune added at request time."
+
+    fortunes = Fortune.all
+    fortunes << fortune
+    fortunes.sort_by! { |fortune| fortune.message || "" }
+
+    # by default this would have been returned as text/html
+    response.content_type = "text/html; charset=UTF-8"
+    render html: Kilt.render("src/views/fortunes.ecr")
+  end
+end

+ 9 - 0
frameworks/Crystal/spider-gazelle/src/models/fortune.cr

@@ -0,0 +1,9 @@
+require "granite/adapter/pg"
+
+class Fortune < Granite::Base
+  adapter pg
+
+  table_name fortune
+  primary id : Int32
+  field message : String
+end

+ 9 - 0
frameworks/Crystal/spider-gazelle/src/models/world.cr

@@ -0,0 +1,9 @@
+require "granite/adapter/pg"
+
+class World < Granite::Base
+  adapter pg
+
+  table_name world
+  primary id : Int32
+  field randomnumber : Int32
+end

+ 20 - 0
frameworks/Crystal/spider-gazelle/src/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>
+		<% fortunes.each do |fortune| %>
+			<tr>
+			<td><%= fortune.id %></td>
+			<td><%= HTML.escape(fortune.message || "") %></td>
+			</tr>
+		<% end %>
+	</table>
+</body>
+</html>