Sfoglia il codice sorgente

Add Clojure duct framework (#3874)

* Add Clojure Duct framework

* Clean up comments

* Disable logging

* Remove useless testing file.

* Update invalid rand number.

* Update platform in benchmark_config

* Remove localhost config.

* Remove development platform config

* Update docker files.
Alexandre Grison 7 anni fa
parent
commit
82eadc9dce
30 ha cambiato i file con 541 aggiunte e 0 eliminazioni
  1. 1 0
      frameworks/Clojure/duct/Procfile
  2. 70 0
      frameworks/Clojure/duct/README.md
  3. 115 0
      frameworks/Clojure/duct/benchmark_config.json
  4. 11 0
      frameworks/Clojure/duct/duct-aleph.dockerfile
  5. 11 0
      frameworks/Clojure/duct/duct-httpkit.dockerfile
  6. 11 0
      frameworks/Clojure/duct/duct-immutant.dockerfile
  7. 11 0
      frameworks/Clojure/duct/duct-mongodb.dockerfile
  8. 11 0
      frameworks/Clojure/duct/duct.dockerfile
  9. 2 0
      frameworks/Clojure/duct/profiles.clj
  10. 35 0
      frameworks/Clojure/duct/project.clj
  11. 50 0
      frameworks/Clojure/duct/resources/hello/config.edn
  12. 6 0
      frameworks/Clojure/duct/resources/hello/db-mongo.edn
  13. 14 0
      frameworks/Clojure/duct/resources/hello/db-pg.edn
  14. 2 0
      frameworks/Clojure/duct/resources/hello/db.edn
  15. 1 0
      frameworks/Clojure/duct/resources/hello/server-aleph.edn
  16. 1 0
      frameworks/Clojure/duct/resources/hello/server-httpkit.edn
  17. 3 0
      frameworks/Clojure/duct/resources/hello/server-immutant.edn
  18. 1 0
      frameworks/Clojure/duct/resources/hello/server-jetty.edn
  19. 1 0
      frameworks/Clojure/duct/resources/hello/server.edn
  20. 10 0
      frameworks/Clojure/duct/resources/logback.xml
  21. 17 0
      frameworks/Clojure/duct/src/hello/boundary/fortune_db.clj
  22. 32 0
      frameworks/Clojure/duct/src/hello/boundary/world_db.clj
  23. 41 0
      frameworks/Clojure/duct/src/hello/handler/fortunes.clj
  24. 10 0
      frameworks/Clojure/duct/src/hello/handler/fortunes_mongodb.clj
  25. 12 0
      frameworks/Clojure/duct/src/hello/handler/plaintext.clj
  26. 22 0
      frameworks/Clojure/duct/src/hello/handler/queries.clj
  27. 12 0
      frameworks/Clojure/duct/src/hello/handler/queries_mongodb.clj
  28. 8 0
      frameworks/Clojure/duct/src/hello/handler/single_query.clj
  29. 8 0
      frameworks/Clojure/duct/src/hello/handler/single_query_mongodb.clj
  30. 12 0
      frameworks/Clojure/duct/src/hello/main.clj

+ 1 - 0
frameworks/Clojure/duct/Procfile

@@ -0,0 +1 @@
+web: java $JVM_OPTS -cp target/hello-duct-standalone.jar clojure.main -m hello.main

+ 70 - 0
frameworks/Clojure/duct/README.md

@@ -0,0 +1,70 @@
+# Duct Benchmarking Test
+
+Implementation of the benchmark using the Clojure [Duct framework](https://github.com/duct-framework).
+
+## Test Type Implementation Source Code
+
+* [JSON](resources/hello/config.edn)
+* [PLAINTEXT](resources/hello/config.edn)
+* [DB](src/hello/handler/single_query.clj)
+* [QUERY](src/hello/handler/queries.clj)
+* [FORTUNES](src/hello/handler/fortunes.clj)
+
+## Important Libraries
+The tests were run with these versions:
+
+```edn
+  :dependencies [[org.clojure/clojure "1.9.0"]
+                 [duct/core "0.6.2"]
+                 [duct/module.logging "0.3.1"]
+                 [duct/module.web "0.6.4"]
+                 [duct/module.ataraxy "0.2.0"]
+                 [duct/module.sql "0.4.2"]
+                 [duct/handler.sql "0.3.1"]
+                 [duct/database.sql.hikaricp "0.3.3"]
+                 [hiccup "1.0.5"]
+                 ; alternate servers
+                 [duct/server.http.http-kit "0.1.2"]
+                 [duct/server.http.aleph "0.1.2"]
+                 [me.grison/duct-immutant "0.1.0"]
+                 ; tested databases
+                 [org.postgresql/postgresql "42.1.4"]
+                 [me.grison/duct-mongodb "0.1.1"]]
+```
+
+See **project.clj** for more information.
+
+## Database
+
+For the moment it test both PostgreSQL and MongoDB.
+
+Implementation for *Jetty*, *Aleph*, *HTTP-Kit* and *Immutant* are part of this project.
+
+## Test URLs
+### JSON
+
+[http://localhost:3000/json](http://localhost:3000/json)
+
+### PLAINTEXT
+
+[http://localhost:3000/plaintext](http://localhost:3000/plaintext)
+
+### DB
+
+[http://localhost:3000/db](http://localhost:3000/db)
+[http://localhost:3000/db-mongo](http://localhost:3000/db-mongo)
+
+### QUERY
+
+[http://localhost:3000/queries?queries=](http://localhost:3000/queries?queries=)
+[http://localhost:3000/queries-mongo?queries=](http://localhost:3000/queries-mongo?queries=)
+
+### FORTUNES
+
+[http://localhost:3000/fortunes](http://localhost:3000/fortunes)
+[http://localhost:3000/fortunes-mongo](http://localhost:3000/fortunes-mongo)
+
+## Contributors
+
+- Alexandre Grison - [agrison](https://github.com/agrison) - [@algrison](https://twitter.com/algrison)
+

+ 115 - 0
frameworks/Clojure/duct/benchmark_config.json

@@ -0,0 +1,115 @@
+{
+  "framework": "duct",
+  "tests": [
+    {
+      "default": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "port": 3000,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "duct",
+        "language": "Clojure",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Ring",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "duct",
+        "notes": "",
+        "versus": "None"
+      },
+      "mongodb": {
+        "db_url": "/db-mongo",
+        "query_url": "/queries-mongo?queries=",
+        "fortune_url": "/fortunes-mongo",
+        "port": 3000,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MongoDB",
+        "framework": "duct",
+        "language": "Clojure",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Ring",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "duct-mongodb",
+        "notes": "",
+        "versus": "None"
+      },
+      "httpkit": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "port": 3000,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "duct",
+        "language": "Clojure",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Ring",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "duct-httpkit",
+        "notes": "",
+        "versus": "None"
+      },
+      "aleph": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "port": 3000,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "duct",
+        "language": "Clojure",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Netty",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "duct-aleph",
+        "notes": "",
+        "versus": "None"
+      },
+      "immutant": {
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "port": 3000,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "framework": "duct",
+        "language": "Clojure",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Undertow",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "duct-immutant",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 11 - 0
frameworks/Clojure/duct/duct-aleph.dockerfile

@@ -0,0 +1,11 @@
+FROM clojure:lein-2.8.1
+WORKDIR /duct
+COPY resources resources
+COPY resources/hello/db-pg.edn resources/hello/db.edn
+COPY resources/hello/server-aleph.edn resources/hello/server.edn
+COPY src src
+COPY Procfile Procfile
+COPY project.clj project.clj
+RUN lein uberjar
+
+CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-server", "-jar", "target/hello-duct-standalone.jar"]

+ 11 - 0
frameworks/Clojure/duct/duct-httpkit.dockerfile

@@ -0,0 +1,11 @@
+FROM clojure:lein-2.8.1
+WORKDIR /duct
+COPY resources resources
+COPY resources/hello/db-pg.edn resources/hello/db.edn
+COPY resources/hello/server-httpkit.edn resources/hello/server.edn
+COPY src src
+COPY Procfile Procfile
+COPY project.clj project.clj
+RUN lein uberjar
+
+CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-server", "-jar", "target/hello-duct-standalone.jar"]

+ 11 - 0
frameworks/Clojure/duct/duct-immutant.dockerfile

@@ -0,0 +1,11 @@
+FROM clojure:lein-2.8.1
+WORKDIR /duct
+COPY resources resources
+COPY resources/hello/db-pg.edn resources/hello/db.edn
+COPY resources/hello/server-immutant.edn resources/hello/server.edn
+COPY src src
+COPY Procfile Procfile
+COPY project.clj project.clj
+RUN lein uberjar
+
+CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-server", "-jar", "target/hello-duct-standalone.jar"]

+ 11 - 0
frameworks/Clojure/duct/duct-mongodb.dockerfile

@@ -0,0 +1,11 @@
+FROM clojure:lein-2.8.1
+WORKDIR /duct
+COPY resources resources
+COPY resources/hello/db-mongo.edn resources/hello/db.edn
+COPY resources/hello/server-aleph.edn resources/hello/server.edn
+COPY src src
+COPY Procfile Procfile
+COPY project.clj project.clj
+RUN lein uberjar
+
+CMD ["java", "-server", "-jar", "target/hello-duct-standalone.jar"]

+ 11 - 0
frameworks/Clojure/duct/duct.dockerfile

@@ -0,0 +1,11 @@
+FROM clojure:lein-2.8.1
+WORKDIR /duct
+COPY resources resources
+COPY resources/hello/db-pg.edn resources/hello/db.edn
+COPY resources/hello/server-jetty.edn resources/hello/server.edn
+COPY src src
+COPY Procfile Procfile
+COPY project.clj project.clj
+RUN lein uberjar
+
+CMD ["java", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-XX:+AggressiveOpts", "-server", "-jar", "target/hello-duct-standalone.jar"]

+ 2 - 0
frameworks/Clojure/duct/profiles.clj

@@ -0,0 +1,2 @@
+;; Local profile overrides
+{:profiles/dev {}}

+ 35 - 0
frameworks/Clojure/duct/project.clj

@@ -0,0 +1,35 @@
+(defproject hello "duct"
+  :description "FrameworkBenchmarks test implementations"
+  :url "http://localhost:3000/"
+  :min-lein-version "2.0.0"
+  :dependencies [[org.clojure/clojure "1.9.0"]
+                 [duct/core "0.6.2"]
+                 [duct/module.logging "0.3.1"]
+                 [duct/module.web "0.6.4"]
+                 [duct/module.ataraxy "0.2.0"]
+                 [duct/module.sql "0.4.2"]
+                 [duct/handler.sql "0.3.1"]
+                 [duct/database.sql.hikaricp "0.3.3"]
+                 [hiccup "1.0.5"]
+                 ; alternate servers
+                 [duct/server.http.http-kit "0.1.2"]
+                 [duct/server.http.aleph "0.1.2"]
+                 [me.grison/duct-immutant "0.1.0"]
+                 ; tested databases
+                 [org.postgresql/postgresql "42.1.4"]
+                 [me.grison/duct-mongodb "0.1.1"]]
+  :plugins [[duct/lein-duct "0.10.6"]]
+  :main ^:skip-aot hello.main
+  :resource-paths ["resources" "target/resources"]
+  :prep-tasks     ["javac" "compile" ["run" ":duct/compiler"]]
+  :profiles
+  {:dev  [:project/dev :profiles/dev]
+   :repl {:prep-tasks   ^:replace ["javac" "compile"]
+          :repl-options {:init-ns user}}
+   :uberjar {:aot :all}
+   :profiles/dev {}
+   :project/dev  {:source-paths   ["dev/src"]
+                  :resource-paths ["dev/resources"]
+                  :dependencies   [[integrant/repl "0.2.0"]
+                                   [eftest "0.4.1"]
+                                   [kerodon "0.9.0"]]}})

+ 50 - 0
frameworks/Clojure/duct/resources/hello/config.edn

@@ -0,0 +1,50 @@
+{:duct.core/project-ns       hello
+ :duct.core/environment      :production
+
+ :duct.core/include ["hello/db" "hello/server"]
+
+ :duct.module/logging
+ {}
+
+ :duct.module.web/api
+ {}
+
+ ; for benchmarking performance, disable logging
+ :duct.logger/timbre
+ {:enabled? false
+  :appenders {}
+  :level :error}
+
+ :duct.module/ataraxy
+ {[:get "/json"]                       [:json]
+  [:get "/plaintext"]                  [:plaintext]
+  [:get "/db"]                         [:single-query]
+  [:get "/db-mongo"]                   [:single-query-mongodb]
+  [:get "/queries" #{?queries}]        [:queries]
+  [:get "/queries-mongo" #{?queries}]  [:queries-mongodb]
+  [:get "/fortunes"]                   [:fortunes]
+  [:get "/fortunes-mongo"]             [:fortunes-mongodb]}
+
+ [:duct.handler.static/ok :hello.handler/json]
+ {:body {:message "Hello, World!"}}
+
+ [:duct.handler.static/okplain :hello.handler/plaintext]
+ {:body "Hello, World!"}
+
+ :hello.handler/single-query
+ {:db #ig/ref :duct.database/sql}
+
+ :hello.handler/single-query-mongodb
+ {:db #ig/ref :duct.database.mongodb/monger}
+
+ :hello.handler/queries
+ {:db #ig/ref :duct.database/sql}
+
+ :hello.handler/queries-mongodb
+ {:db #ig/ref :duct.database.mongodb/monger}
+
+ :hello.handler/fortunes
+ {:db #ig/ref :duct.database/sql}
+
+ :hello.handler/fortunes-mongodb
+ {:db #ig/ref :duct.database.mongodb/monger}}

+ 6 - 0
frameworks/Clojure/duct/resources/hello/db-mongo.edn

@@ -0,0 +1,6 @@
+{:duct.database/sql {} ; define it empty
+
+ :duct.database.mongodb/monger
+ {:logger #ig/ref :duct/logger
+  :uri "mongodb://tfb-database:27017/hello_world?username=benchmarkdbuser&password=benchmarkdbpass&maxPoolSize=64"}
+}

+ 14 - 0
frameworks/Clojure/duct/resources/hello/db-pg.edn

@@ -0,0 +1,14 @@
+{
+  :duct.module/sql
+  {:adapter "postgresql"
+   :database-url "jdbc:postgresql://tfb-database:5432/hello_world?user=benchmarkdbuser&password=benchmarkdbpass&ssl=false&loggerLevel=OFF&tcpKeepAlive=true&logUnclosedConnections=false&readOnly=true&disableColumnSanitiser=true&hostRecheckSeconds=15"}
+
+ :duct.database.sql/hikaricp
+  {:auto-commit true
+   :read-only true
+   :connection-timeout 5000
+   :max-lifetime 0
+   :minimum-idle 20
+   :maximum-pool-size 20}
+
+ :duct.database.mongodb/monger {}}

+ 2 - 0
frameworks/Clojure/duct/resources/hello/db.edn

@@ -0,0 +1,2 @@
+; for local dev purpose
+{:duct.core/include ["hello/db-pg"]} ;default

+ 1 - 0
frameworks/Clojure/duct/resources/hello/server-aleph.edn

@@ -0,0 +1 @@
+{:duct.server.http/aleph {}}

+ 1 - 0
frameworks/Clojure/duct/resources/hello/server-httpkit.edn

@@ -0,0 +1 @@
+{:duct.server.http/http-kit {}}

+ 3 - 0
frameworks/Clojure/duct/resources/hello/server-immutant.edn

@@ -0,0 +1,3 @@
+{:duct.server.http/immutant
+ {:virtual-host "tfb-server"
+  :host         "0.0.0.0"}}

+ 1 - 0
frameworks/Clojure/duct/resources/hello/server-jetty.edn

@@ -0,0 +1 @@
+{:duct.server.http/jetty {:ssl? false}}

+ 1 - 0
frameworks/Clojure/duct/resources/hello/server.edn

@@ -0,0 +1 @@
+{:duct.core/include ["hello/server-jetty"]} ;default

+ 10 - 0
frameworks/Clojure/duct/resources/logback.xml

@@ -0,0 +1,10 @@
+<configuration>
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>%msg%n</pattern>
+        </encoder>
+    </appender>
+    <root level="OFF">
+        <appender-ref ref="CONSOLE"/>
+    </root>
+</configuration>

+ 17 - 0
frameworks/Clojure/duct/src/hello/boundary/fortune_db.clj

@@ -0,0 +1,17 @@
+(ns hello.boundary.fortune-db
+  (:require [duct.database.sql]
+            [duct.database.mongodb.monger]
+            [clojure.java.jdbc :as jdbc]
+            [monger.collection :as mc]))
+
+(defprotocol Fortune
+  (get-all [db]))
+
+(extend-protocol Fortune
+  duct.database.sql.Boundary
+  (get-all [{:keys [spec]}]
+    (jdbc/query spec ["select * from fortune"]))
+
+  duct.database.mongodb.monger.Boundary
+  (get-all [{:keys [db]}]
+    (mc/find-maps db "fortune")))

+ 32 - 0
frameworks/Clojure/duct/src/hello/boundary/world_db.clj

@@ -0,0 +1,32 @@
+(ns hello.boundary.world-db
+  (:require [duct.database.sql]
+            [duct.database.mongodb.monger]
+            [clojure.java.jdbc :as jdbc]
+            [monger.collection :as mc]))
+
+(defn- query [db]
+  (first
+    (jdbc/query db ["select * from world where id = ?" (inc (rand-int 10000))])))
+
+(defn- mongo-query [db]
+  (dissoc (mc/find-one-as-map db "world" {:id (inc (rand-int 10000))}) :_id))
+
+(defprotocol World
+  (make-single-query [db])
+  (make-multiple-queries [db num]))
+
+(extend-protocol World
+  duct.database.sql.Boundary
+  (make-single-query [{:keys [spec]}]
+    (query spec))
+
+  (make-multiple-queries [{:keys [spec]} num]
+    (repeatedly num #(query spec))))
+
+(extend-protocol World
+  duct.database.mongodb.monger.Boundary
+  (make-single-query [{:keys [db]}]
+    (mongo-query db))
+
+  (make-multiple-queries [{:keys [db]} num]
+    (repeatedly num #(mongo-query db))))

+ 41 - 0
frameworks/Clojure/duct/src/hello/handler/fortunes.clj

@@ -0,0 +1,41 @@
+(ns hello.handler.fortunes
+  (:require [integrant.core :as ig]
+            [ataraxy.response :as response]
+            [hello.boundary.fortune-db :as fortune-db]))
+
+; copied from pedestal implementation
+(defn prepare-fortunes
+  [fortunes]
+  (sort-by :message
+           (conj fortunes
+                 {:id 0 :message "Additional fortune added at request time."})))
+
+(def ^String base-fortune-pre "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>")
+(def ^String base-fortune-post "</table></body></html>")
+(defn fortunes-str
+  "The HTML bit for this is very very small;
+  Opt to create the HTML string by hand in a tight loop rather than using Hiccup"
+  [fortunes]
+  (let [sb (StringBuilder. ^String base-fortune-pre)]
+    (doseq [{:keys [id message]} fortunes]
+      (.append sb "<tr><td>")
+      (.append sb (str (int id)))
+      (.append sb "</td><td>")
+      (dotimes [c-idx (count message)]
+        (let [c (.charAt ^String message c-idx)]
+          (case c
+            \& (.append sb "&amp;")
+            \" (.append sb "&quot;")
+            \' (.append sb "&apos;")
+            \< (.append sb "&lt;")
+            \> (.append sb "&gt;")
+            (.append sb c))))
+      (.append sb "</td></tr>"))
+    (.append sb base-fortune-post)
+    (.toString sb)))
+
+(defmethod ig/init-key :hello.handler/fortunes [_ {:keys [db]}]
+  (fn [{[_] :ataraxy/result}]
+    (let [fortunes (prepare-fortunes (fortune-db/get-all db))]
+      [::response/ok (fortunes-str fortunes)])))
+

+ 10 - 0
frameworks/Clojure/duct/src/hello/handler/fortunes_mongodb.clj

@@ -0,0 +1,10 @@
+(ns hello.handler.fortunes-mongodb
+  (:require [integrant.core :as ig]
+            [ataraxy.response :as response]
+            [hello.boundary.fortune-db :as fortune-db]
+            [hello.handler.fortunes :refer [prepare-fortunes fortunes-str]]))
+
+(defmethod ig/init-key :hello.handler/fortunes-mongodb [_ {:keys [db]}]
+  (fn [{[_] :ataraxy/result}]
+    (let [fortunes (prepare-fortunes (fortune-db/get-all db))]
+      [::response/ok (fortunes-str fortunes)])))

+ 12 - 0
frameworks/Clojure/duct/src/hello/handler/plaintext.clj

@@ -0,0 +1,12 @@
+(ns hello.handler.plaintext
+  (:require [integrant.core :as ig]
+            [ataraxy.handler :as handler]
+            [ataraxy.response :as ataraxy-resp]
+            [ring.util.response :as resp]))
+
+; Create a specific init-key extending ::ok, patching the Content-Type header
+; so that it equals "text/plain" instead of "application/octet-stream"
+(defmethod ig/init-key :duct.handler.static/okplain [_ response]
+  (let [val (ig/init-key :duct.handler.static/ok response)
+        patched (update-in (val response) [:headers "Content-Type"] (constantly "text/plain"))]
+    (constantly patched)))

+ 22 - 0
frameworks/Clojure/duct/src/hello/handler/queries.clj

@@ -0,0 +1,22 @@
+(ns hello.handler.queries
+  (:require [integrant.core :as ig]
+            [ataraxy.response :as response]
+            [hello.boundary.world-db :as world-db]))
+
+; taken from the luminus sample
+(defn query-count
+  "Parse provided string value of query count, clamping values to between 1 and 500."
+  [^String queries]
+  (let [n ^long (try (Integer/parseInt queries)
+                     (catch Exception _ 1))] ; default to 1 on parse failure
+    (cond
+      (< ^long n 1) 1
+      (> ^long n 500) 500
+      :else n)))
+
+(defmethod ig/init-key :hello.handler/queries [_ {:keys [db]}]
+  (fn [request]
+    [::response/ok
+     (let [queries (get-in request [:params :queries] "1")
+           num (query-count queries)]
+       (world-db/make-multiple-queries db num))]))

+ 12 - 0
frameworks/Clojure/duct/src/hello/handler/queries_mongodb.clj

@@ -0,0 +1,12 @@
+(ns hello.handler.queries-mongodb
+  (:require [integrant.core :as ig]
+            [ataraxy.response :as response]
+            [hello.boundary.world-db :as world-db]
+            [hello.handler.queries :refer [query-count]]))
+
+(defmethod ig/init-key :hello.handler/queries-mongodb [_ {:keys [db]}]
+  (fn [request]
+    [::response/ok
+     (let [queries (get-in request [:params :queries] "1")
+           num (query-count queries)]
+       (world-db/make-multiple-queries db num))]))

+ 8 - 0
frameworks/Clojure/duct/src/hello/handler/single_query.clj

@@ -0,0 +1,8 @@
+(ns hello.handler.single-query
+  (:require [integrant.core :as ig]
+            [ataraxy.response :as response]
+            [hello.boundary.world-db :as world-db]))
+
+(defmethod ig/init-key :hello.handler/single-query [_ {:keys [db]}]
+  (fn [{[_] :ataraxy/result}]
+    [::response/ok (world-db/make-single-query db)]))

+ 8 - 0
frameworks/Clojure/duct/src/hello/handler/single_query_mongodb.clj

@@ -0,0 +1,8 @@
+(ns hello.handler.single-query-mongodb
+  (:require [integrant.core :as ig]
+            [ataraxy.response :as response]
+            [hello.boundary.world-db :as world-db]))
+
+(defmethod ig/init-key :hello.handler/single-query-mongodb [_ {:keys [db]}]
+  (fn [{[_] :ataraxy/result}]
+    [::response/ok (world-db/make-single-query db)]))

+ 12 - 0
frameworks/Clojure/duct/src/hello/main.clj

@@ -0,0 +1,12 @@
+(ns hello.main
+  (:gen-class)
+  (:require [clojure.java.io :as io]
+            [duct.core :as duct]))
+
+(duct/load-hierarchy)
+
+(defn -main [& args]
+  (let [keys (or (duct/parse-keys args) [:duct/daemon])]
+    (-> (duct/read-config (io/resource "hello/config.edn"))
+        (duct/prep keys)
+        (duct/exec keys))))