Przeglądaj źródła

benchmark for the Macchiato ClojureScript framework (#2715)

* updated Luminus benchmark

benchmark cleanup

updated db config

cleanup

* added macchiato example

* updated for macchiato benchmark

* added macchiato example

* updated for macchiato benchmark

* cleanup

* fixed release build for macchiato

* added macchiato to .travis.yml

* fixed typo

* fix for json serialization, escaping

* cleanup

* added pool size configuration

* fixed typo

* added leiningen and java dependencies

* fixed typo

* updated to set server header, added utf-8 content type

* fixed warnings, added doctype for macchiato
Dmitri Sotnikov 8 lat temu
rodzic
commit
dd87fd261b

+ 1 - 0
.travis.yml

@@ -36,6 +36,7 @@ env:
     - "TESTDIR=Clojure/compojure"
     - "TESTDIR=Clojure/http-kit"
     - "TESTDIR=Clojure/luminus"
+    - "TESTDIR=Clojure/macchiato"
     - "TESTDIR=Clojure/pedestal"
     - "TESTDIR=Clojure/aleph"
     - "TESTDIR=Crystal/crystal"

+ 29 - 0
frameworks/Clojure/macchiato/benchmark_config.json

@@ -0,0 +1,29 @@
+{
+  "framework": "macchiato",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "db_url": "/db",
+      "query_url": "/queries/",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates/",
+      "port": 3000,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "macchiato",
+      "language": "ClojureScript",
+      "flavor": "Macchiato",
+      "orm": "Raw",
+      "platform": "nodejs",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "macchiato",
+      "notes": "",
+      "versus": "nodejs"
+    }
+  }]
+}

+ 49 - 0
frameworks/Clojure/macchiato/hello/README.md

@@ -0,0 +1,49 @@
+## Welcome to hello
+
+### Prequisites
+
+[Node.js](https://nodejs.org/en/) needs to be installed to run the application.
+
+### running in development mode
+
+run the following command in the terminal to install NPM modules and start Figwheel:
+
+```
+lein build
+```
+
+run `node` in another terminal:
+
+```
+npm start
+```
+
+#### configuring the REPL
+
+Once Figwheel and node are running, you can connect to the remote REPL at `localhost:7000`.
+
+Type `(cljs)` in the REPL to connect to Figwheel ClojureScript REPL.
+
+
+### building the release version
+
+```
+lein package
+```
+
+Run the release version:
+
+```
+npm start
+```
+
+### Running using Docker
+
+The template comes with a `Dockerfile` for running the application using Docker
+
+Once you've run `lein package`, you can build and run a Docker container as follows:
+
+```
+docker build -t hello:latest .
+docker run -p 3000:3000 hello:latest
+```

+ 13 - 0
frameworks/Clojure/macchiato/hello/env/dev/hello/app.cljs

@@ -0,0 +1,13 @@
+ (ns ^:figwheel-always hello.app
+  (:require
+    [hello.core :as core]
+    [cljs.nodejs :as node]
+    [mount.core :as mount]))
+
+(mount/in-cljc-mode)
+
+(cljs.nodejs/enable-util-print!)
+
+(.on js/process "uncaughtException" #(js/console.error %))
+
+(set! *main-cli-fn* core/main)

+ 11 - 0
frameworks/Clojure/macchiato/hello/env/dev/user.clj

@@ -0,0 +1,11 @@
+(ns user
+  (:require [figwheel-sidecar.repl-api :as ra]))
+
+(defn start-fw []
+  (ra/start-figwheel!))
+
+(defn stop-fw []
+  (ra/stop-figwheel!))
+
+(defn cljs []
+  (ra/cljs-repl))

+ 11 - 0
frameworks/Clojure/macchiato/hello/env/prod/hello/app.cljs

@@ -0,0 +1,11 @@
+ (ns hello.app
+  (:require
+    [hello.core :as core]
+    [cljs.nodejs]
+    [mount.core :as mount]))
+
+(mount/in-cljc-mode)
+
+(cljs.nodejs/enable-util-print!)
+
+(set! *main-cli-fn* core/main)

+ 8 - 0
frameworks/Clojure/macchiato/hello/env/test/hello/app.cljs

@@ -0,0 +1,8 @@
+(ns hello.app
+  (:require
+    [doo.runner :refer-macros [doo-tests]]
+    [hello.core-test]))
+
+(doo-tests 'hello.core-test)
+
+

+ 86 - 0
frameworks/Clojure/macchiato/hello/project.clj

@@ -0,0 +1,86 @@
+(defproject hello "0.1.0-SNAPSHOT"
+  :description "FIXME: write this!"
+  :url "http://example.com/FIXME"
+  :dependencies [[bidi "2.0.16"]
+                 [com.cemerick/piggieback "0.2.1"]
+                 [com.taoensso/timbre "4.8.0"]
+                 [hiccups "0.3.0"]
+                 [macchiato/core "0.1.6"]
+                 [macchiato/env "0.0.5"]
+                 [mount "0.1.11"]
+                 [org.clojure/clojure "1.8.0"]
+                 [org.clojure/clojurescript "1.9.518"]]
+  :jvm-opts ^:replace ["-Xmx1g" "-server"]
+  :plugins [[lein-doo "0.1.7"]
+            [macchiato/lein-npm "0.6.3"]
+            [lein-figwheel "0.5.10"]
+            [lein-cljsbuild "1.1.5"]]
+  :npm {:dependencies [[pg "6.1.5"]
+                       [sequelize "3.30.4"]
+                       [source-map-support "0.4.6"]]
+        :write-package-json true}
+  :source-paths ["src" "target/classes"]
+  :clean-targets ["target"]
+  :target-path "target"
+  :profiles
+  {:dev
+   {:npm {:package {:main "target/out/hello.js"
+                    :scripts {:start "node target/out/hello.js"}}}
+    :cljsbuild
+    {:builds {:dev
+              {:source-paths ["env/dev" "src"]
+               :figwheel     true
+               :compiler     {:main          hello.app
+                              :output-to     "target/out/hello.js"
+                              :output-dir    "target/out"
+                              :target        :nodejs
+                              :optimizations :none
+                              :pretty-print  true
+                              :source-map    true
+                              :source-map-timestamp false}}}}
+    :figwheel
+    {:http-server-root "public"
+     :nrepl-port 7000
+     :reload-clj-files {:clj false :cljc true}
+     :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
+    :source-paths ["env/dev"]
+    :repl-options {:init-ns user}}
+   :test
+   {:cljsbuild
+    {:builds
+     {:test
+      {:source-paths ["env/test" "src" "test"]
+       :compiler     {:main hello.app
+                      :output-to     "target/test/hello.js"
+                      :target        :nodejs
+                      :optimizations :none
+                      :pretty-print  true
+                      :source-map    true}}}}
+    :doo {:build "test"}}
+   :release
+   {:npm {:package {:main "target/release/hello.js"
+                    :scripts {:start "node target/release/hello.js"}}}
+    :cljsbuild
+    {:builds
+     {:release
+      {:source-paths ["env/prod" "src"]
+       :compiler     {:main          hello.app
+                      :output-to     "target/release/hello.js"
+                      :language-in   :ecmascript5
+                      ;:output-dir    "target/release"
+                      :target        :nodejs
+                      :optimizations :simple ;:none
+                      :pretty-print  false}}}}}}
+  :aliases
+  {"build" ["do"
+            ["clean"]
+            ["npm" "install"]
+            ["figwheel" "dev"]]
+   "package" ["do"
+              ["clean"]
+              ["npm" "install"]
+              ["with-profile" "release" "npm" "init" "-y"]
+              ["with-profile" "release" "cljsbuild" "once"]]
+   "test" ["do"
+           ["npm" "install"]
+           ["with-profile" "test" "doo" "node"]]})

+ 6 - 0
frameworks/Clojure/macchiato/hello/src/hello/config.cljs

@@ -0,0 +1,6 @@
+(ns hello.config
+  (:require [macchiato.env :as config]
+            [mount.core :refer [defstate]]))
+
+(defstate env :start (config/env))
+

+ 21 - 0
frameworks/Clojure/macchiato/hello/src/hello/core.cljs

@@ -0,0 +1,21 @@
+(ns hello.core
+  (:require
+    [hello.config :refer [env]]
+    [hello.middleware :refer [wrap-defaults]]
+    [hello.routes :refer [router]]
+    [macchiato.server :as http]
+    [macchiato.session.memory :as mem]
+    [mount.core :as mount :refer [defstate]]
+    [taoensso.timbre :refer-macros [log trace debug info warn error fatal]]))
+
+(defn app []
+  (mount/start)
+  (let [host (or (:host @env) "127.0.0.1")
+        port (or (some-> @env :port js/parseInt) 3000)]
+    (http/start
+      {:handler    (wrap-defaults router)
+       :host       host
+       :port       port
+       :on-success #(info "hello started on" host ":" port)})))
+
+(defn main [& args] (app))

+ 108 - 0
frameworks/Clojure/macchiato/hello/src/hello/db.cljs

@@ -0,0 +1,108 @@
+(ns hello.db
+  (:require
+    [cljs.nodejs :as node]
+    [mount.core :refer [defstate]]))
+
+(def sequelize (node/require "sequelize"))
+
+(defstate db
+  :start (sequelize.
+           "hello_world"
+           "benchmarkdbuser"
+           "benchmarkdbpass"
+           #js {:host    "localhost"
+                :dialect "postgres"
+                :logging false
+                :pool #js {:max 64
+                           :min 1
+                           :idle 1000}}))
+
+(defstate worlds
+  :start (.define @db
+                  "World"
+                  #js{:id           #js {:type       "Sequelize.INTEGER"
+                                         :primaryKey true}
+                      :randomnumber {:type "Sequelize.INTEGER"}}
+                  #js {:timestamps      false
+                       :freezeTableName true
+                       :tableName       "World"}))
+
+(defstate fortunes
+  :start (.define @db
+                  "Fortune"
+                  #js {:id      #js {:type       "Sequelize.INTEGER"
+                                     :primaryKey true}
+                       :message {:type "Sequelize.STRING"}}
+                  #js {:timestamps      false
+                       :freezeTableName true
+                       :tableName       "Fortune"}))
+
+(defn all-fortunes [handler error-handler]
+  (-> @fortunes
+      (.findAll #js {:raw true})
+      (.then #(handler (js->clj % :keywordize-keys true)))
+      (.catch error-handler)))
+
+(defn world [id handler error-handler]
+  (-> @worlds
+      (.findOne (clj->js {:where {:id id} :raw true}))
+      (.then handler)
+      (.catch error-handler)))
+
+(defn update-world! [world handler error-handler]
+  (-> @worlds
+      (.update (clj->js {:randomnumber (:randomnumber world)})
+               (clj->js {:where {:id (:id world)} :raw true}))
+      (.then #(when handler (handler (js->clj world))))
+      (.catch error-handler)))
+
+(defn get-query-count
+  "Parse provided string value of query count, clamping values to between 1 and 500."
+  [queries]
+  (let [n (js/parseInt queries)]                            ; default to 1 on parse failure
+    (cond
+      (js/isNaN n) 1
+      (< n 1) 1
+      (> n 500) 500
+      :else n)))
+
+(defn run-query [handler error-handler]
+  (world (unchecked-inc (rand-int 10000)) handler error-handler))
+
+(defn run-queries
+  "Run the specified number of queries, return the results"
+  [queries handler error-handler]
+  (-> (get-query-count queries)
+      (repeatedly #(world (unchecked-inc (rand-int 10000)) identity error-handler))
+      (js/Promise.all)
+      (.then handler)
+      (.catch error-handler)))
+
+(defn get-fortunes [handler error-handler]
+  "Fetch the full list of Fortunes from the database, sort them by the fortune
+ message text, and then return the results."
+  (all-fortunes
+    (fn [result]
+      (handler
+        (sort-by
+          :message
+          (conj result {:id 0 :message "Additional fortune added at request time."}))))
+    error-handler))
+
+(defn set-random-number!
+  "set a new randomNumber, persist, and return the record"
+  [error-handler world]
+  (let [w (assoc world :randomnumber (unchecked-inc (rand-int 9999)))]
+    (update-world! w nil error-handler)
+    w))
+
+(defn update-and-persist
+  "Changes the :randomnumber of a number of world entities.
+  Persists the changes to sql then returns the updated entities"
+  [queries handler error-handler]
+  (run-queries
+    queries
+    (fn [results]
+      (handler (clj->js (mapv (partial set-random-number! error-handler) (js->clj results)))))
+    error-handler))
+

+ 9 - 0
frameworks/Clojure/macchiato/hello/src/hello/middleware.cljs

@@ -0,0 +1,9 @@
+(ns hello.middleware
+  (:require
+    [macchiato.util.response :as r]))
+
+(defn wrap-defaults [handler]
+  (fn [req res raise]
+    (handler req #(res (assoc-in % [:headers "Server"] "macchiato")) raise)))
+
+

+ 99 - 0
frameworks/Clojure/macchiato/hello/src/hello/routes.cljs

@@ -0,0 +1,99 @@
+(ns hello.routes
+  (:require
+    [bidi.bidi :as bidi]
+    [clojure.string :as st]
+    [hello.db :as db]
+    [hiccups.runtime]
+    [macchiato.util.response :as r])
+  (:require-macros
+    [hiccups.core :refer [html]]))
+
+
+(defn not-found [req res raise]
+  (-> (html
+        [:html
+         [:body
+          [:h2 (:uri req) " was not found"]]])
+      (r/not-found)
+      (r/content-type "text/html")
+      (res)))
+
+(defn json-serialization
+  "Test 1: JSON serialization"
+  [_ res _]
+  (-> (js/JSON.stringify #js {:message "Hello, World!"})
+      (r/ok)
+      (r/content-type "application/json")
+      (res)))
+
+(defn plaintext [_ res _]
+  (-> (r/ok "Hello, World!")
+      (r/content-type "text/html")
+      (res)))
+
+(defn single-query-test [req res raise]
+  (db/run-query
+    #(-> (js/JSON.stringify %)
+         (r/ok)
+         (r/content-type "application/json")
+         (res))
+    raise))
+
+(defn escape-html [s]
+  (st/escape s
+             {"&"  "&amp;"
+              ">"  "&gt;"
+              "<"  "&lt;"
+              "\"" "&quot;"}))
+
+(defn fortunes-test [req res raise]
+  (db/get-fortunes
+    #(-> (str
+           "<!doctype html>"
+           (html
+             [:html
+              [:head [:title "Fortunes"]]
+              [:body
+               [:table
+                [:tr [:th "id"] [:th "message"]]
+                (for [message %]
+                  [:tr [:td (:id message)] [:td (-> message :message escape-html)]])]]]))
+         (r/ok)
+         (r/content-type "text/html;charset=utf-8")
+         (res))
+    raise))
+
+(defn queries-test [req res raise]
+  (db/run-queries
+    (or (-> req :route-params :queries) 1)
+    #(-> (js/JSON.stringify %)
+         (r/ok)
+         (r/content-type "application/json")
+         (res))
+    raise))
+
+(defn update-test [req res raise]
+  (db/update-and-persist
+    (or (-> req :route-params :queries) 1)
+    #(-> (js/JSON.stringify %)
+         (r/ok)
+         (r/content-type "application/json")
+         (res))
+    raise))
+
+(def routes
+  ["/" {""          {:get (fn [_ res _] (res (r/ok "Hello, World!")))}
+        "plaintext" {:get plaintext}
+        "json"      {:get json-serialization}
+        "db"        {:get single-query-test}
+        "fortunes"  {:get fortunes-test}
+        "queries"
+                    {"/"            {:get queries-test}
+                     ["/" :queries] {:get queries-test}}
+        "updates"   {"/"            {:get update-test}
+                     ["/" :queries] {:get update-test}}}])
+
+(defn router [req res raise]
+  (if-let [{:keys [handler route-params]} (bidi/match-route* routes (:uri req) req)]
+    (handler (assoc req :route-params route-params) res raise)
+    (not-found req res raise)))

+ 10 - 0
frameworks/Clojure/macchiato/setup.sh

@@ -0,0 +1,10 @@
+#!/bin/bash
+
+fw_depends postgresql nodejs java leiningen
+
+# Update db host in the source file
+sed -i 's|localhost|'"${DBHOST}"'|g' hello/src/hello/db.cljs
+
+cd hello
+lein package
+node target/release/hello.js &

+ 12 - 0
frameworks/Clojure/macchiato/source_code

@@ -0,0 +1,12 @@
+./hello/env/dev/hello/app.cljs
+./hello/env/dev/user.clj
+./hello/env/prod/hello/app.cljs
+./hello/env/test/hello/app.cljs
+./hello/package.json
+./hello/project.clj
+./hello/README.md
+./hello/src/hello/config.cljs
+./hello/src/hello/core.cljs
+./hello/src/hello/db.cljs
+./hello/src/hello/middleware.cljs
+./hello/src/hello/routes.cljs