Browse Source

Rust axum optimizations (#7304)

* Rust axum mongo and pg improvements

* Rust axum cleanup

* Rust axum mongo performance fix

* rust axum refactorings

* rust axum env variables for max/min pool size

* Rust axum refactorings

* rust axum mongo raw

* rust axum mongo using dynamic vector
Dragos Varovici 3 years ago
parent
commit
1ad1797620
30 changed files with 1838 additions and 554 deletions
  1. 432 83
      frameworks/Rust/axum/Cargo.lock
  2. 36 29
      frameworks/Rust/axum/Cargo.toml
  3. 25 0
      frameworks/Rust/axum/axum-mongo-raw.dockerfile
  4. 3 1
      frameworks/Rust/axum/axum-mongo.dockerfile
  5. 24 0
      frameworks/Rust/axum/axum-pg-pool.dockerfile
  6. 2 2
      frameworks/Rust/axum/axum-pg.dockerfile
  7. 3 1
      frameworks/Rust/axum/axum-sqlx.dockerfile
  8. 1 1
      frameworks/Rust/axum/axum.dockerfile
  9. 47 7
      frameworks/Rust/axum/benchmark_config.json
  10. 0 1
      frameworks/Rust/axum/src/common.rs
  11. 0 39
      frameworks/Rust/axum/src/database_bb8.rs
  12. 96 7
      frameworks/Rust/axum/src/database_mongo.rs
  13. 96 0
      frameworks/Rust/axum/src/database_mongo_raw.rs
  14. 207 0
      frameworks/Rust/axum/src/database_pg.rs
  15. 124 0
      frameworks/Rust/axum/src/database_pg_pool.rs
  16. 62 9
      frameworks/Rust/axum/src/database_sqlx.rs
  17. 9 2
      frameworks/Rust/axum/src/main.rs
  18. 0 177
      frameworks/Rust/axum/src/main_bb8.rs
  19. 106 60
      frameworks/Rust/axum/src/main_mongo.rs
  20. 162 0
      frameworks/Rust/axum/src/main_mongo_raw.rs
  21. 119 0
      frameworks/Rust/axum/src/main_pg.rs
  22. 171 0
      frameworks/Rust/axum/src/main_pg_pool.rs
  23. 34 93
      frameworks/Rust/axum/src/main_sqlx.rs
  24. 2 2
      frameworks/Rust/axum/src/models_common.rs
  25. 3 6
      frameworks/Rust/axum/src/models_mongo.rs
  26. 16 0
      frameworks/Rust/axum/src/models_pg.rs
  27. 2 5
      frameworks/Rust/axum/src/models_pg_pool.rs
  28. 2 5
      frameworks/Rust/axum/src/models_sqlx.rs
  29. 9 9
      frameworks/Rust/axum/src/server.rs
  30. 45 15
      frameworks/Rust/axum/src/utils.rs

+ 432 - 83
frameworks/Rust/axum/Cargo.lock

@@ -2,6 +2,12 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
 [[package]]
 name = "ahash"
 version = "0.7.6"
@@ -32,6 +38,138 @@ dependencies = [
  "yansi-term",
 ]
 
+[[package]]
+name = "async-channel"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+ "slab",
+]
+
+[[package]]
+name = "async-global-executor"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c290043c9a95b05d45e952fb6383c67bcb61471f60cfa21e890dba6654234f43"
+dependencies = [
+ "async-channel",
+ "async-executor",
+ "async-io",
+ "async-mutex",
+ "blocking",
+ "futures-lite",
+ "num_cpus",
+ "once_cell",
+]
+
+[[package]]
+name = "async-io"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b"
+dependencies = [
+ "concurrent-queue",
+ "futures-lite",
+ "libc",
+ "log",
+ "once_cell",
+ "parking",
+ "polling",
+ "slab",
+ "socket2",
+ "waker-fn",
+ "winapi",
+]
+
+[[package]]
+name = "async-lock"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-mutex"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-std"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c"
+dependencies = [
+ "async-channel",
+ "async-global-executor",
+ "async-io",
+ "async-lock",
+ "crossbeam-utils",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-lite",
+ "gloo-timers",
+ "kv-log-macro",
+ "log",
+ "memchr",
+ "num_cpus",
+ "once_cell",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-task"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9"
+
 [[package]]
 name = "async-trait"
 version = "0.1.53"
@@ -52,6 +190,12 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "atomic-waker"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
+
 [[package]]
 name = "autocfg"
 version = "1.1.0"
@@ -60,18 +204,21 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "axum"
-version = "0.1.2"
+version = "0.2.0"
 dependencies = [
+ "async-std",
+ "async-stream",
  "async-trait",
  "axum 0.5.4",
- "bb8",
- "bb8-postgres",
+ "axum-core",
+ "deadpool",
+ "deadpool-postgres",
  "dotenv",
  "futures",
  "futures-util",
  "hyper",
  "mongodb",
- "num",
+ "num_cpus",
  "rand",
  "serde",
  "serde_derive",
@@ -82,7 +229,7 @@ dependencies = [
  "tokio-pg-mapper-derive",
  "tokio-postgres",
  "tower",
- "tower-http",
+ "tower-http 0.2.5",
  "yarte",
 ]
 
@@ -112,7 +259,7 @@ dependencies = [
  "sync_wrapper",
  "tokio",
  "tower",
- "tower-http",
+ "tower-http 0.3.1",
  "tower-layer",
  "tower-service",
 ]
@@ -137,31 +284,6 @@ version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
 
-[[package]]
-name = "bb8"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1627eccf3aa91405435ba240be23513eeca466b5dc33866422672264de061582"
-dependencies = [
- "async-trait",
- "futures-channel",
- "futures-util",
- "parking_lot 0.12.0",
- "tokio",
-]
-
-[[package]]
-name = "bb8-postgres"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0a22f6e28b0cf30a1861b69e3defedad0b98991a5bc54ab11bb0c2e20927686"
-dependencies = [
- "async-trait",
- "bb8",
- "tokio",
- "tokio-postgres",
-]
-
 [[package]]
 name = "bitflags"
 version = "1.3.2"
@@ -177,6 +299,20 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "blocking"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+]
+
 [[package]]
 name = "bson"
 version = "2.2.0"
@@ -220,11 +356,20 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
 
+[[package]]
+name = "cache-padded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
+
 [[package]]
 name = "cc"
 version = "1.0.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+dependencies = [
+ "jobserver",
+]
 
 [[package]]
 name = "cfg-if"
@@ -245,6 +390,15 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "concurrent-queue"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
+dependencies = [
+ "cache-padded",
+]
+
 [[package]]
 name = "convert_case"
 version = "0.4.0"
@@ -291,6 +445,15 @@ version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403"
 
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "crossbeam-queue"
 version = "0.3.5"
@@ -321,6 +484,16 @@ dependencies = [
  "typenum",
 ]
 
+[[package]]
+name = "ctor"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
+dependencies = [
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "darling"
 version = "0.13.4"
@@ -362,6 +535,41 @@ version = "2.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
 
+[[package]]
+name = "deadpool"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a90d58a15f5acfe41afcac9775d8e92f2338d14482220c778c6e42aa77778182"
+dependencies = [
+ "async-trait",
+ "deadpool-runtime",
+ "num_cpus",
+ "retain_mut",
+ "serde",
+ "tokio",
+]
+
+[[package]]
+name = "deadpool-postgres"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c668a58063c6331e3437e3146970943ad82b1b36169fd979bb2645ac2088209a"
+dependencies = [
+ "deadpool",
+ "log",
+ "tokio",
+ "tokio-postgres",
+]
+
+[[package]]
+name = "deadpool-runtime"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaa37046cc0f6c3cc6090fbdbf73ef0b8ef4cfcc37f6befc0020f63e8cf121e1"
+dependencies = [
+ "tokio",
+]
+
 [[package]]
 name = "derivative"
 version = "2.2.0"
@@ -468,6 +676,18 @@ dependencies = [
  "instant",
 ]
 
+[[package]]
+name = "flate2"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af"
+dependencies = [
+ "cfg-if",
+ "crc32fast",
+ "libc",
+ "miniz_oxide",
+]
+
 [[package]]
 name = "fnv"
 version = "1.0.7"
@@ -558,6 +778,21 @@ version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
 
+[[package]]
+name = "futures-lite"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
+]
+
 [[package]]
 name = "futures-macro"
 version = "0.3.21"
@@ -620,6 +855,18 @@ dependencies = [
  "wasi 0.10.2+wasi-snapshot-preview1",
 ]
 
+[[package]]
+name = "gloo-timers"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "js-sys",
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "hashbrown"
 version = "0.11.2"
@@ -693,9 +940,9 @@ dependencies = [
 
 [[package]]
 name = "http"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
+checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb"
 dependencies = [
  "bytes",
  "fnv",
@@ -721,9 +968,9 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
 
 [[package]]
 name = "httparse"
-version = "1.7.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6330e8a36bd8c859f3fa6d9382911fbb7147ec39807f63b923933a247240b9ba"
+checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
 
 [[package]]
 name = "httpdate"
@@ -829,6 +1076,15 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
 
+[[package]]
+name = "jobserver"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "js-sys"
 version = "0.3.57"
@@ -838,6 +1094,15 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "kv-log-macro"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
+dependencies = [
+ "log",
+]
+
 [[package]]
 name = "lazy_static"
 version = "1.4.0"
@@ -873,6 +1138,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
 dependencies = [
  "cfg-if",
+ "value-bag",
 ]
 
 [[package]]
@@ -929,6 +1195,15 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
+[[package]]
+name = "miniz_oxide"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
+dependencies = [
+ "adler",
+]
+
 [[package]]
 name = "mio"
 version = "0.8.2"
@@ -964,6 +1239,7 @@ dependencies = [
  "bson",
  "chrono",
  "derivative",
+ "flate2",
  "futures-core",
  "futures-executor",
  "futures-io",
@@ -984,6 +1260,7 @@ dependencies = [
  "serde_with",
  "sha-1",
  "sha2",
+ "snap",
  "socket2",
  "stringprep",
  "strsim",
@@ -997,6 +1274,7 @@ dependencies = [
  "typed-builder",
  "uuid",
  "webpki-roots",
+ "zstd",
 ]
 
 [[package]]
@@ -1036,28 +1314,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "num"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
-dependencies = [
- "num-complex",
- "num-integer",
- "num-iter",
- "num-rational",
- "num-traits",
-]
-
-[[package]]
-name = "num-complex"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085"
-dependencies = [
- "num-traits",
-]
-
 [[package]]
 name = "num-integer"
 version = "0.1.44"
@@ -1068,28 +1324,6 @@ dependencies = [
  "num-traits",
 ]
 
-[[package]]
-name = "num-iter"
-version = "0.1.43"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-rational"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
 [[package]]
 name = "num-traits"
 version = "0.2.14"
@@ -1158,6 +1392,12 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "parking"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
+
 [[package]]
 name = "parking_lot"
 version = "0.11.2"
@@ -1283,6 +1523,19 @@ version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
 
+[[package]]
+name = "polling"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "log",
+ "wepoll-ffi",
+ "winapi",
+]
+
 [[package]]
 name = "postgres-protocol"
 version = "0.6.3"
@@ -1438,6 +1691,12 @@ dependencies = [
  "quick-error",
 ]
 
+[[package]]
+name = "retain_mut"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c31b5c4033f8fdde8700e4657be2c497e7288f01515be52168c631e2e4d4086"
+
 [[package]]
 name = "ring"
 version = "0.16.20"
@@ -1709,6 +1968,12 @@ version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
 
+[[package]]
+name = "snap"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45456094d1983e2ee2a18fdfebce3189fa451699d0502cb8e3b49dba5ba41451"
+
 [[package]]
 name = "socket2"
 version = "0.4.4"
@@ -1932,9 +2197,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
-version = "1.17.0"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
+checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b"
 dependencies = [
  "bytes",
  "libc",
@@ -2091,9 +2356,27 @@ dependencies = [
 
 [[package]]
 name = "tower-http"
-version = "0.3.0"
+version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79dd37121c38240c4b4fe6520332406218bbf876f2f690fe9e406020189366fd"
+checksum = "aba3f3efabf7fb41fae8534fc20a817013dd1c12cb45441efb6c82e6556b4cd8"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-range-header",
+ "pin-project-lite",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7755f984aa46a9d50829dc84b6a674b2f3b8b4de47c0708923953b090d48acc"
 dependencies = [
  "bitflags",
  "bytes",
@@ -2315,6 +2598,16 @@ dependencies = [
  "v_escape",
 ]
 
+[[package]]
+name = "value-bag"
+version = "1.0.0-alpha.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f"
+dependencies = [
+ "ctor",
+ "version_check",
+]
+
 [[package]]
 name = "vcpkg"
 version = "0.2.15"
@@ -2327,6 +2620,12 @@ version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
+[[package]]
+name = "waker-fn"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+
 [[package]]
 name = "want"
 version = "0.3.0"
@@ -2374,6 +2673,18 @@ dependencies = [
  "wasm-bindgen-shared",
 ]
 
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
 [[package]]
 name = "wasm-bindgen-macro"
 version = "0.2.80"
@@ -2432,6 +2743,15 @@ dependencies = [
  "webpki",
 ]
 
+[[package]]
+name = "wepoll-ffi"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "whoami"
 version = "1.2.1"
@@ -2615,3 +2935,32 @@ dependencies = [
  "unicode-xid",
  "yarte_helpers",
 ]
+
+[[package]]
+name = "zstd"
+version = "0.11.1+zstd.1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a16b8414fde0414e90c612eba70985577451c4c504b99885ebed24762cb81a"
+dependencies = [
+ "zstd-safe",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "5.0.1+zstd.1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c12659121420dd6365c5c3de4901f97145b79651fb1d25814020ed2ed0585ae"
+dependencies = [
+ "libc",
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.1+zstd.1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b"
+dependencies = [
+ "cc",
+ "libc",
+]

+ 36 - 29
frameworks/Rust/axum/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = "axum"
-version = "0.1.2"
+version = "0.2.0"
 authors = ["Dragos Varovici <[email protected]>"]
 edition = "2021"
 
@@ -13,40 +13,47 @@ name = "axum-sqlx"
 path = "src/main_sqlx.rs"
 
 [[bin]]
-name = "axum-bb8"
-path = "src/main_bb8.rs"
+name = "axum-pg-pool"
+path = "src/main_pg_pool.rs"
 
 [[bin]]
 name = "axum-mongo"
 path = "src/main_mongo.rs"
 
+[[bin]]
+name = "axum-mongo-raw"
+path = "src/main_mongo_raw.rs"
+
+[[bin]]
+name = "axum-pg"
+path = "src/main_pg.rs"
+
 [dependencies]
-rand = { version = "^0.8", features = ["small_rng"] }
-yarte = { version = "^0.15" }
-async-trait = { version = "0.1" }
-futures = { version = "^0.3" }
-futures-util = { version = "^0.3" }
-dotenv = { version = "^0.15" }
-num = { version = "0.4", default-features = false }
-serde = { version = "^1", features = ["derive"] }
-serde_json = { version = "^1" }
-serde_derive = { version = "^1" }
-axum = { version = "0.5" }
-tokio = { version = "1.0", features = ["full"] }
-hyper = "0.14"
-tower = { version = "0.4", features = ["util"] }
-tower-http = { version = "0.3", features = ["set-header"] }
-sqlx = { version = "^0.5", features = [
-    "postgres",
-    "macros",
-    "runtime-tokio-native-tls",
-] }
-bb8 = "0.8"
-bb8-postgres = "0.8"
-tokio-postgres = "0.7"
-tokio-pg-mapper = "0.2"
-tokio-pg-mapper-derive = "0.2"
-mongodb = "2.0"
+num_cpus = "1.13.1"
+rand = { version = "0.8.5", features = ["small_rng"] }
+yarte = "0.15.6"
+async-stream = "0.3.3"
+async-trait = "0.1.53"
+async-std = "1.11.0"
+futures = "0.3.21"
+futures-util = "0.3.21"
+dotenv = "0.15.0"
+serde = { version = "1.0.136", features = ["derive"] }
+serde_json = "1.0.79"
+serde_derive = "1.0.136"
+axum = "0.5.1"
+tokio = { version = "1.17.0", features = ["full"] }
+hyper = "0.14.18"
+tower = { version = "0.4.12", features = ["util"] }
+tower-http = { version = "0.2.5", features = ["set-header"] }
+sqlx = { version = "0.5.12", features = [ "postgres", "macros", "runtime-tokio-native-tls" ] }
+tokio-postgres = "0.7.5"
+tokio-pg-mapper = "0.2.0"
+tokio-pg-mapper-derive = "0.2.0"
+mongodb = { version = "2.2.1", features = [ "zstd-compression", "snappy-compression", "zlib-compression" ] }
+axum-core = "0.2.2"
+deadpool = { version = "0.9.3", features = ["rt_tokio_1", "serde", "async-trait", "managed" ] }
+deadpool-postgres = "0.10.2"
 
 [profile.release]
 lto = true

+ 25 - 0
frameworks/Rust/axum/axum-mongo-raw.dockerfile

@@ -0,0 +1,25 @@
+FROM rust:1.60-slim-buster
+
+ENV AXUM_TECHEMPOWER_MONGODB_URL=mongodb://tfb-database:27017
+ENV AXUM_TECHEMPOWER_MAX_POOL_SIZE=28
+ENV AXUM_TECHEMPOWER_MIN_POOL_SIZE=14
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+    pkg-config libssl-dev \
+    && rm -rf /var/lib/apt/lists/*
+
+WORKDIR /axum
+COPY ./src ./src
+COPY ./templates ./templates
+COPY ./Cargo.toml ./Cargo.toml
+COPY ./Cargo.lock ./Cargo.lock
+COPY ./run.sh ./run.sh
+RUN chmod +x ./run.sh
+
+ENV RUSTFLAGS "-C target-cpu=native"
+RUN cargo build --release
+RUN cp ./target/release/axum-mongo-raw ./target/release/axum-techempower
+
+EXPOSE 8000
+
+CMD ["./run.sh"]

+ 3 - 1
frameworks/Rust/axum/axum-mongo.dockerfile

@@ -1,6 +1,8 @@
-FROM rust:1.59-slim-bullseye
+FROM rust:1.60-slim-buster
 
 ENV AXUM_TECHEMPOWER_MONGODB_URL=mongodb://tfb-database:27017
+ENV AXUM_TECHEMPOWER_MAX_POOL_SIZE=28
+ENV AXUM_TECHEMPOWER_MIN_POOL_SIZE=14
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
     pkg-config libssl-dev \

+ 24 - 0
frameworks/Rust/axum/axum-pg-pool.dockerfile

@@ -0,0 +1,24 @@
+FROM rust:1.60-slim-buster
+
+ENV AXUM_TECHEMPOWER_DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world
+ENV AXUM_TECHEMPOWER_MAX_POOL_SIZE=28
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+    libpq-dev pkg-config libssl-dev \
+    && rm -rf /var/lib/apt/lists/*
+
+WORKDIR /axum
+COPY ./src ./src
+COPY ./templates ./templates
+COPY ./Cargo.toml ./Cargo.toml
+COPY ./Cargo.lock ./Cargo.lock
+COPY ./run.sh ./run.sh
+RUN chmod +x ./run.sh
+
+ENV RUSTFLAGS "-C target-cpu=native"
+RUN cargo build --release
+RUN cp ./target/release/axum-pg-pool ./target/release/axum-techempower
+
+EXPOSE 8000
+
+CMD ["./run.sh"]

+ 2 - 2
frameworks/Rust/axum/axum-bb8.dockerfile → frameworks/Rust/axum/axum-pg.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.59-slim-bullseye
+FROM rust:1.60-slim-buster
 
 ENV AXUM_TECHEMPOWER_DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world
 
@@ -16,7 +16,7 @@ RUN chmod +x ./run.sh
 
 ENV RUSTFLAGS "-C target-cpu=native"
 RUN cargo build --release
-RUN cp ./target/release/axum-bb8 ./target/release/axum-techempower
+RUN cp ./target/release/axum-pg ./target/release/axum-techempower
 
 EXPOSE 8000
 

+ 3 - 1
frameworks/Rust/axum/axum-sqlx.dockerfile

@@ -1,6 +1,8 @@
-FROM rust:1.59-slim-bullseye
+FROM rust:1.60-slim-buster
 
 ENV AXUM_TECHEMPOWER_DATABASE_URL=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world
+ENV AXUM_TECHEMPOWER_MAX_POOL_SIZE=56
+ENV AXUM_TECHEMPOWER_MIN_POOL_SIZE=56
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
     libpq-dev pkg-config libssl-dev \

+ 1 - 1
frameworks/Rust/axum/axum.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.59-slim-bullseye
+FROM rust:1.60-slim-buster
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
     pkg-config libssl-dev \

+ 47 - 7
frameworks/Rust/axum/benchmark_config.json

@@ -22,6 +22,25 @@
         "versus": "None"
       },
       "sqlx": {
+        "db_url": "/db",
+        "fortune_url": "/fortunes",
+        "port": 8000,
+        "approach": "Realistic",
+        "classification": "Fullstack",
+        "database": "postgres",
+        "framework": "Axum",
+        "language": "Rust",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Rust",
+        "webserver": "Hyper",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Axum [Postgresql - sqlx]",
+        "notes": "",
+        "versus": "None"
+      },
+      "pg": {
         "db_url": "/db",
         "fortune_url": "/fortunes",
         "query_url": "/queries?queries=",
@@ -33,16 +52,16 @@
         "framework": "Axum",
         "language": "Rust",
         "flavor": "None",
-        "orm": "Full",
+        "orm": "Raw",
         "platform": "Rust",
         "webserver": "Hyper",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "Axum [sqlx]",
+        "display_name": "Axum [Postgresql]",
         "notes": "",
         "versus": "None"
       },
-      "bb8": {
+      "pg-pool": {
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "update_url": "/updates?queries=",
@@ -54,12 +73,12 @@
         "framework": "Axum",
         "language": "Rust",
         "flavor": "None",
-        "orm": "Full",
+        "orm": "Raw",
         "platform": "Rust",
         "webserver": "Hyper",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "Axum [bb8]",
+        "display_name": "Axum [Postgresql - deadpool]",
         "notes": "",
         "versus": "None"
       },
@@ -67,6 +86,7 @@
         "db_url": "/db",
         "query_url": "/queries?queries=",
         "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
         "port": 8000,
         "approach": "Realistic",
         "classification": "Fullstack",
@@ -74,12 +94,32 @@
         "framework": "Axum",
         "language": "Rust",
         "flavor": "None",
-        "orm": "Full",
+        "orm": "Raw",
+        "platform": "Rust",
+        "webserver": "Hyper",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Axum [Mongodb]",
+        "notes": "",
+        "versus": "None"
+      },
+      "mongo-raw": {
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "port": 8000,
+        "approach": "Realistic",
+        "classification": "Fullstack",
+        "database": "mongodb",
+        "framework": "Axum",
+        "language": "Rust",
+        "flavor": "None",
+        "orm": "Raw",
         "platform": "Rust",
         "webserver": "Hyper",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "Axum [mongodb]",
+        "display_name": "Axum [Mongodb raw]",
         "notes": "",
         "versus": "None"
       }

+ 0 - 1
frameworks/Rust/axum/src/common.rs

@@ -1 +0,0 @@
-pub const POOL_SIZE: u32 = 56;

+ 0 - 39
frameworks/Rust/axum/src/database_bb8.rs

@@ -1,39 +0,0 @@
-use axum::extract::{Extension, FromRequest, RequestParts};
-use axum::http::StatusCode;
-
-use async_trait::async_trait;
-use bb8::{Pool, PooledConnection};
-use bb8_postgres::PostgresConnectionManager;
-use bb8_postgres::tokio_postgres::NoTls;
-use crate::common::POOL_SIZE;
-use crate::utils::internal_error;
-
-pub type ConnectionManager = PostgresConnectionManager<NoTls>;
-pub type ConnectionPool = Pool<ConnectionManager>;
-pub type Connection = PooledConnection<'static, ConnectionManager>;
-
-pub async fn create_bb8_pool(database_url: String) -> ConnectionPool {
-    let manager = PostgresConnectionManager::new_from_stringlike(database_url, NoTls).unwrap();
-
-    Pool::builder().max_size(POOL_SIZE).build(manager).await.unwrap()
-}
-
-pub struct DatabaseConnection(pub Connection);
-
-#[async_trait]
-impl<B> FromRequest<B> for DatabaseConnection
-    where
-        B: Send,
-{
-    type Rejection = (StatusCode, String);
-
-    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
-        let Extension(pool) = Extension::<ConnectionPool>::from_request(req)
-            .await
-            .map_err(internal_error)?;
-
-        let conn = pool.get_owned().await.map_err(internal_error)?;
-
-        Ok(Self(conn))
-    }
-}

+ 96 - 7
frameworks/Rust/axum/src/database_mongo.rs

@@ -1,27 +1,116 @@
 use axum::extract::{Extension, FromRequest, RequestParts};
 use axum::http::StatusCode;
+use futures_util::stream::FuturesUnordered;
+use futures_util::TryStreamExt;
+use std::io;
 
-use async_trait::async_trait;
-use mongodb::{Client, Database};
 use crate::utils::internal_error;
+use crate::{Fortune, World};
+use futures_util::StreamExt;
+use mongodb::bson::doc;
+use mongodb::Database;
 
 pub struct DatabaseConnection(pub Database);
 
 #[async_trait]
 impl<B> FromRequest<B> for DatabaseConnection
-    where
-        B: Send,
+where
+    B: Send,
 {
     type Rejection = (StatusCode, String);
 
     async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
-        let Extension(client) = Extension::<Client>::from_request(req)
+        let Extension(db) = Extension::<Database>::from_request(req)
             .await
             .map_err(internal_error)?;
 
-        let database = client.database("hello_world");
+        Ok(Self(db))
+    }
+}
+
+#[derive(Debug)]
+pub enum MongoError {
+    Io(io::Error),
+    Mongo(mongodb::error::Error),
+}
 
-        Ok(Self(database))
+impl From<io::Error> for MongoError {
+    fn from(err: io::Error) -> Self {
+        MongoError::Io(err)
     }
 }
 
+impl From<mongodb::error::Error> for MongoError {
+    fn from(err: mongodb::error::Error) -> Self {
+        MongoError::Mongo(err)
+    }
+}
+
+pub async fn find_world_by_id(db: Database, id: i32) -> Result<World, MongoError> {
+    let world_collection = db.collection::<World>("world");
+
+    let filter = doc! { "_id": id as f32 };
+
+    let world: World = world_collection
+        .find_one(Some(filter), None)
+        .await
+        .unwrap()
+        .expect("expected world, found none");
+    Ok(world)
+}
+
+pub async fn find_worlds(db: Database, ids: Vec<i32>) -> Result<Vec<World>, MongoError> {
+    let future_worlds = FuturesUnordered::new();
+
+    for id in ids {
+        future_worlds.push(find_world_by_id(db.clone(), id));
+    }
+
+    let worlds: Result<Vec<World>, MongoError> = future_worlds.try_collect().await;
+    worlds
+}
+
+pub async fn fetch_fortunes(db: Database) -> Result<Vec<Fortune>, MongoError> {
+    let fortune_collection = db.collection::<Fortune>("fortune");
+
+    let mut fortune_cursor = fortune_collection
+        .find(None, None)
+        .await
+        .expect("fortunes could not be loaded");
+
+    let mut fortunes: Vec<Fortune> = Vec::new();
+
+    while let Some(doc) = fortune_cursor.next().await {
+        fortunes.push(doc.expect("could not load fortune"));
+    }
+
+    fortunes.push(Fortune {
+        id: 0.0,
+        message: "Additional fortune added at request time.".to_string(),
+    });
+
+    fortunes.sort_by(|a, b| a.message.cmp(&b.message));
+    Ok(fortunes)
+}
+
+pub async fn update_worlds(
+    db: Database,
+    worlds: Vec<World>,
+) -> Result<bool, MongoError> {
+    let mut updates = Vec::new();
+
+    for world in worlds {
+        updates.push(doc! {
+        "q": { "id": world.id }, "u": { "$set": { "randomNumber": world.random_number }}
+        });
+    }
+
+    db.run_command(
+        doc! {"update": "world", "updates": updates, "ordered": false},
+        None,
+    )
+    .await
+    .expect("could not update worlds");
+
+    Ok(true)
+}

+ 96 - 0
frameworks/Rust/axum/src/database_mongo_raw.rs

@@ -0,0 +1,96 @@
+use axum::extract::{Extension, FromRequest, RequestParts};
+use axum::http::StatusCode;
+use futures_util::stream::FuturesUnordered;
+use futures_util::TryStreamExt;
+use std::io;
+
+use crate::utils::internal_error;
+use crate::{World};
+use mongodb::bson::{doc, RawDocumentBuf};
+use mongodb::Database;
+
+pub struct DatabaseConnection(pub Database);
+
+#[async_trait]
+impl<B> FromRequest<B> for DatabaseConnection
+where
+    B: Send,
+{
+    type Rejection = (StatusCode, String);
+
+    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
+        let Extension(db) = Extension::<Database>::from_request(req)
+            .await
+            .map_err(internal_error)?;
+
+        Ok(Self(db))
+    }
+}
+
+#[derive(Debug)]
+pub enum MongoError {
+    Io(io::Error),
+    Mongo(mongodb::error::Error),
+}
+
+impl From<io::Error> for MongoError {
+    fn from(err: io::Error) -> Self {
+        MongoError::Io(err)
+    }
+}
+
+impl From<mongodb::error::Error> for MongoError {
+    fn from(err: mongodb::error::Error) -> Self {
+        MongoError::Mongo(err)
+    }
+}
+
+pub async fn find_world_by_id(db: Database, id: i32) -> Result<World, MongoError> {
+    let world_collection = db.collection::<RawDocumentBuf>("world");
+
+    let filter = doc! { "_id": id as f32 };
+
+    let raw: RawDocumentBuf = world_collection
+        .find_one(Some(filter), None)
+        .await
+        .unwrap()
+        .expect("expected world, found none");
+
+    Ok(World {
+        id: raw.get("id").expect("expected to parse world id").expect("could not get world id").as_f64().expect("could not extract world id") as f32,
+        random_number: raw.get("randomNumber").expect("expected to parse world randomNumber").expect("expected to get world randomNumber").as_f64().expect("could not extract world randomNumber") as f32,
+    })
+}
+
+pub async fn find_worlds(db: Database, ids: Vec<i32>) -> Result<Vec<World>, MongoError> {
+    let future_worlds = FuturesUnordered::new();
+
+    for id in ids {
+        future_worlds.push(find_world_by_id(db.clone(), id));
+    }
+
+    let worlds: Result<Vec<World>, MongoError> = future_worlds.try_collect().await;
+    worlds
+}
+
+pub async fn update_worlds(
+    db: Database,
+    worlds: Vec<World>,
+) -> Result<bool, MongoError> {
+    let mut updates = Vec::new();
+
+    for world in worlds {
+        updates.push(doc! {
+        "q": { "id": world.id }, "u": { "$set": { "randomNumber": world.random_number }}
+        });
+    }
+
+    db.run_command(
+        doc! {"update": "world", "updates": updates, "ordered": false},
+        None,
+    )
+    .await
+    .expect("could not update worlds");
+
+    Ok(true)
+}

+ 207 - 0
frameworks/Rust/axum/src/database_pg.rs

@@ -0,0 +1,207 @@
+use axum::extract::{Extension, FromRequest, RequestParts};
+use axum::http::StatusCode;
+use futures::{
+    stream::futures_unordered::FuturesUnordered, FutureExt, StreamExt, TryStreamExt,
+};
+use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng};
+use std::sync::Arc;
+use std::{collections::HashMap, fmt::Write, io};
+use tokio::pin;
+use tokio_postgres::{connect, types::ToSql, Client, NoTls, Statement};
+
+use crate::models_pg::{Fortune, World};
+use crate::utils::internal_error;
+
+#[derive(Debug)]
+pub enum PgError {
+    Io(io::Error),
+    Pg(tokio_postgres::Error),
+}
+
+impl From<io::Error> for PgError {
+    fn from(err: io::Error) -> Self {
+        PgError::Io(err)
+    }
+}
+
+impl From<tokio_postgres::Error> for PgError {
+    fn from(err: tokio_postgres::Error) -> Self {
+        PgError::Pg(err)
+    }
+}
+
+/// Postgres interface
+pub struct PgConnection {
+    client: Client,
+    fortune: Statement,
+    world: Statement,
+    updates: HashMap<u16, Statement>,
+}
+
+impl PgConnection {
+    pub async fn connect(db_url: String) -> Arc<PgConnection> {
+        let (cl, conn) = connect(&*db_url, NoTls)
+            .await
+            .expect("can not connect to postgresql");
+
+        // Spawn connection
+        tokio::spawn(async move {
+            if let Err(error) = conn.await {
+                eprintln!("Connection error: {}", error);
+            }
+        });
+
+        let fortune = cl.prepare("SELECT * FROM fortune").await.unwrap();
+        let mut updates = HashMap::new();
+
+        for num in 1..=500u16 {
+            let mut pl = 1;
+            let mut q = String::new();
+
+            q.push_str("UPDATE world SET randomnumber = CASE id ");
+
+            for _ in 1..=num {
+                let _ = write!(q, "when ${} then ${} ", pl, pl + 1);
+                pl += 2;
+            }
+
+            q.push_str("ELSE randomnumber END WHERE id IN (");
+
+            for _ in 1..=num {
+                let _ = write!(q, "${},", pl);
+                pl += 1;
+            }
+
+            q.pop();
+            q.push(')');
+
+            updates.insert(num, cl.prepare(&q).await.unwrap());
+        }
+
+        let world = cl.prepare("SELECT * FROM world WHERE id=$1").await.unwrap();
+
+        Arc::new(PgConnection {
+            client: cl,
+            fortune,
+            world,
+            updates,
+        })
+    }
+}
+
+impl PgConnection {
+    async fn query_one_world(&self, id: i32) -> Result<World, PgError> {
+        let stream = self.client.query_raw(&self.world, &[&id]).await?;
+        pin!(stream);
+        let row = stream.next().await.unwrap()?;
+        Ok(World {
+            id: row.get(0),
+            randomnumber: row.get(1),
+        })
+    }
+
+    pub async fn get_world(&self) -> Result<World, PgError> {
+        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+
+        let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+
+        let world = self.query_one_world(random_id).await?;
+        Ok(world)
+    }
+
+    pub async fn get_worlds(&self, num: usize) -> Result<Vec<World>, PgError> {
+        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+
+        let worlds = FuturesUnordered::new();
+
+        for _ in 0..num {
+            let w_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+            worlds.push(self.query_one_world(w_id));
+        }
+
+        worlds.try_collect().await
+    }
+
+    pub async fn update(&self, num: u16) -> Result<Vec<World>, PgError> {
+        let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+
+        let worlds = FuturesUnordered::new();
+
+        for _ in 0..num {
+            let id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+            let w_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+
+            worlds.push(self.query_one_world(w_id).map(move |res| match res {
+                Ok(mut world) => {
+                    world.randomnumber = id;
+                    Ok(world)
+                }
+
+                Err(err) => Err(err),
+            }));
+        }
+
+        let st = self.updates.get(&num).unwrap().clone();
+
+        let worlds: Vec<World> = worlds.try_collect().await?;
+
+        let mut params: Vec<&(dyn ToSql + Sync)> = Vec::with_capacity(num as usize * 3);
+
+        for w in &worlds {
+            params.push(&w.id);
+            params.push(&w.randomnumber);
+        }
+
+        for w in &worlds {
+            params.push(&w.id);
+        }
+
+        self.client.query(&st, &params[..]).await?;
+
+        Ok(worlds)
+    }
+
+    pub async fn tell_fortune(&self) -> Result<Vec<Fortune>, PgError> {
+        let mut items = vec![Fortune {
+            id: 0,
+            message: "Additional fortune added at request time.".parse().unwrap(),
+        }];
+
+        let fut = self.client.query_raw::<_, _, &[i32; 0]>(&self.fortune, &[]);
+
+        let stream = fut.await?;
+        pin!(stream);
+
+        while let Some(row) = stream.next().await {
+            let row = row?;
+
+            items.push(Fortune {
+                id: row.get(0),
+                message: row.get(1),
+            });
+        }
+
+        items.sort_by(|it, next| it.message.cmp(&next.message));
+        Ok(items)
+    }
+}
+
+pub struct DatabaseConnection(pub Arc<PgConnection>);
+
+#[async_trait]
+impl<B> FromRequest<B> for DatabaseConnection
+where
+    B: Send,
+{
+    type Rejection = (StatusCode, String);
+
+    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
+        let Extension(pg_connection) = Extension::<Arc<PgConnection>>::from_request(req)
+            .await
+            .map_err(internal_error)?;
+
+        let conn = pg_connection.clone();
+
+        Ok(Self(conn))
+    }
+}

+ 124 - 0
frameworks/Rust/axum/src/database_pg_pool.rs

@@ -0,0 +1,124 @@
+use axum::extract::{Extension, FromRequest, RequestParts};
+use axum::http::StatusCode;
+use deadpool_postgres::{Client, Manager, ManagerConfig, RecyclingMethod};
+use std::io;
+use std::str::FromStr;
+use tokio_pg_mapper::FromTokioPostgresRow;
+use tokio_postgres::{NoTls, Row, Statement};
+
+use crate::utils::internal_error;
+use crate::{Fortune, World};
+
+#[derive(Debug)]
+pub enum PgError {
+    Io(io::Error),
+    Pg(tokio_postgres::Error),
+}
+
+impl From<io::Error> for PgError {
+    fn from(err: io::Error) -> Self {
+        PgError::Io(err)
+    }
+}
+
+impl From<tokio_postgres::Error> for PgError {
+    fn from(err: tokio_postgres::Error) -> Self {
+        PgError::Pg(err)
+    }
+}
+
+pub async fn create_pool(
+    database_url: String,
+    max_pool_size: u32,
+) -> deadpool_postgres::Pool {
+    let pg_config =
+        tokio_postgres::Config::from_str(&*database_url).expect("invalid database url");
+
+    let mgr_config = ManagerConfig {
+        recycling_method: RecyclingMethod::Fast,
+    };
+    let mgr = Manager::from_config(pg_config, NoTls, mgr_config);
+    let pool: deadpool_postgres::Pool = deadpool_postgres::Pool::builder(mgr)
+        .max_size(max_pool_size as usize)
+        .build()
+        .unwrap();
+
+    pool
+}
+
+pub struct DatabaseClient(pub Client);
+
+#[async_trait]
+impl<B> FromRequest<B> for DatabaseClient
+where
+    B: Send,
+{
+    type Rejection = (StatusCode, String);
+
+    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
+        let Extension(pool) = Extension::<deadpool_postgres::Pool>::from_request(req)
+            .await
+            .map_err(internal_error)?;
+
+        let conn = pool.get().await.map_err(internal_error)?;
+
+        Ok(Self(conn))
+    }
+}
+
+pub async fn fetch_world_by_id(
+    client: &Client,
+    number: i32,
+    select: &Statement,
+) -> Result<World, PgError> {
+    let row: Row = client.query_one(select, &[&number]).await.unwrap();
+
+    Ok(World::from_row(row).unwrap())
+}
+
+pub async fn update_world(
+    client: &Client,
+    update: &Statement,
+    random_id: i32,
+    w_id: i32,
+) -> Result<u64, PgError> {
+    let rows_modified: u64 = client.execute(update, &[&random_id, &w_id]).await.unwrap();
+
+    Ok(rows_modified)
+}
+
+pub async fn fetch_all_fortunes(
+    client: Client,
+    select: &Statement,
+) -> Result<Vec<Fortune>, PgError> {
+    let rows: Vec<Row> = client.query(select, &[]).await.unwrap();
+
+    let mut fortunes: Vec<Fortune> = Vec::with_capacity(rows.capacity());
+
+    for row in rows {
+        fortunes.push(Fortune::from_row(row).unwrap());
+    }
+
+    Ok(fortunes)
+}
+
+pub async fn prepare_fetch_all_fortunes_statement(client: &Client) -> Statement {
+    client
+        .prepare_cached("SELECT * FROM Fortune")
+        .await
+        .unwrap()
+}
+
+pub async fn prepare_fetch_world_by_id_statement(client: &Client) -> Statement {
+    client
+        .prepare_cached("SELECT id, randomnumber FROM World WHERE id = $1")
+        .await
+        .unwrap()
+}
+
+pub async fn prepare_update_world_by_id_statement(client: &Client) -> Statement {
+    client
+        .prepare_cached("UPDATE World SET randomnumber = $1 WHERE id = $2")
+        .await
+        .unwrap()
+}

+ 62 - 9
frameworks/Rust/axum/src/database_sqlx.rs

@@ -1,23 +1,50 @@
 use axum::extract::{Extension, FromRequest, RequestParts};
 use axum::http::StatusCode;
+use std::io;
 
-use async_trait::async_trait;
-use sqlx::{PgPool, Postgres};
-use sqlx::pool::PoolConnection;
-use sqlx::postgres::PgPoolOptions;
-use crate::common::POOL_SIZE;
 use crate::utils::internal_error;
+use crate::{Fortune, World};
+use sqlx::pool::PoolConnection;
+use sqlx::postgres::{PgArguments, PgPoolOptions};
+use sqlx::{Arguments, PgPool, Postgres};
+
+#[derive(Debug)]
+pub enum PgError {
+    Io(io::Error),
+    Pg(sqlx::Error),
+}
 
-pub async fn create_pool(database_url: String) -> PgPool {
-    PgPoolOptions::new().max_connections(POOL_SIZE).min_connections(56).connect(&*database_url).await.unwrap()
+impl From<io::Error> for PgError {
+    fn from(err: io::Error) -> Self {
+        PgError::Io(err)
+    }
+}
+
+impl From<sqlx::Error> for PgError {
+    fn from(err: sqlx::Error) -> Self {
+        PgError::Pg(err)
+    }
+}
+
+pub async fn create_pool(
+    database_url: String,
+    max_pool_size: u32,
+    min_pool_size: u32,
+) -> PgPool {
+    PgPoolOptions::new()
+        .max_connections(max_pool_size)
+        .min_connections(min_pool_size)
+        .connect(&*database_url)
+        .await
+        .unwrap()
 }
 
 pub struct DatabaseConnection(pub PoolConnection<Postgres>);
 
 #[async_trait]
 impl<B> FromRequest<B> for DatabaseConnection
-    where
-        B: Send,
+where
+    B: Send,
 {
     type Rejection = (StatusCode, String);
 
@@ -32,3 +59,29 @@ impl<B> FromRequest<B> for DatabaseConnection
     }
 }
 
+pub async fn fetch_world(
+    mut conn: PoolConnection<Postgres>,
+    number: i32,
+) -> Result<World, PgError> {
+    let mut args = PgArguments::default();
+    args.add(number);
+
+    let world: World =
+        sqlx::query_as_with("SELECT id, randomnumber FROM World WHERE id = $1", args)
+            .fetch_one(&mut conn)
+            .await
+            .ok()
+            .expect("error loading world");
+    Ok(world)
+}
+
+pub async fn fetch_fortunes(
+    mut conn: PoolConnection<Postgres>,
+) -> Result<Vec<Fortune>, PgError> {
+    let fortunes: Vec<Fortune> = sqlx::query_as("SELECT * FROM Fortune")
+        .fetch_all(&mut conn)
+        .await
+        .ok()
+        .expect("error loading Fortunes");
+    Ok(fortunes)
+}

+ 9 - 2
frameworks/Rust/axum/src/main.rs

@@ -1,4 +1,9 @@
-mod common;
+extern crate async_trait;
+extern crate dotenv;
+extern crate serde_derive;
+extern crate tokio_pg_mapper;
+extern crate tokio_pg_mapper_derive;
+
 mod models_common;
 mod server;
 
@@ -28,12 +33,14 @@ pub async fn json() -> impl IntoResponse {
 async fn main() {
     dotenv().ok();
 
+    let server_header_value = HeaderValue::from_static("Axum");
+
     let app = Router::new()
         .route("/plaintext", get(plaintext))
         .route("/json", get(json))
         .layer(SetResponseHeaderLayer::if_not_present(
             header::SERVER,
-            HeaderValue::from_static("Axum"),
+            server_header_value,
         ));
 
     server::builder()

+ 0 - 177
frameworks/Rust/axum/src/main_bb8.rs

@@ -1,177 +0,0 @@
-mod common;
-mod database_bb8;
-mod models_bb8;
-mod models_common;
-mod server;
-mod utils;
-
-use crate::database_bb8::{create_bb8_pool, Connection, DatabaseConnection};
-use axum::http::{header, HeaderValue};
-use axum::{
-    extract::Query, http::StatusCode, response::IntoResponse, routing::get, Extension,
-    Json, Router,
-};
-use bb8_postgres::tokio_postgres::{Row, Statement};
-use dotenv::dotenv;
-use rand::rngs::SmallRng;
-use rand::SeedableRng;
-use std::env;
-use tokio_pg_mapper::FromTokioPostgresRow;
-use tower_http::set_header::SetResponseHeaderLayer;
-use yarte::Template;
-
-use crate::utils::Utf8Html;
-use models_bb8::{Fortune, World};
-use utils::{parse_params, random_number, Params};
-
-async fn db(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse {
-    let mut rng = SmallRng::from_entropy();
-    let number = random_number(&mut rng);
-
-    let select = prepare_fetch_world_by_id_statement(&conn).await;
-    let world = fetch_world_by_id_using_statement(&conn, number, &select).await;
-
-    (StatusCode::OK, Json(world))
-}
-
-async fn fetch_world_by_id_using_statement(
-    conn: &Connection,
-    number: i32,
-    select: &Statement,
-) -> World {
-    let row: Row = conn.query_one(select, &[&number]).await.unwrap();
-
-    World::from_row(row).unwrap()
-}
-
-async fn queries(
-    DatabaseConnection(conn): DatabaseConnection,
-    Query(params): Query<Params>,
-) -> impl IntoResponse {
-    let q = parse_params(params);
-
-    let mut rng = SmallRng::from_entropy();
-
-    let mut results = Vec::with_capacity(q);
-
-    let select = prepare_fetch_world_by_id_statement(&conn).await;
-
-    for _ in 0..q {
-        let query_id = random_number(&mut rng);
-
-        let result: World =
-            fetch_world_by_id_using_statement(&conn, query_id, &select).await;
-
-        results.push(result);
-    }
-
-    (StatusCode::OK, Json(results))
-}
-
-async fn fortunes(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse {
-    let select = prepare_fetch_all_fortunes_statement(&conn).await;
-
-    let rows: Vec<Row> = conn.query(&select, &[]).await.unwrap();
-
-    let mut fortunes: Vec<Fortune> = Vec::with_capacity(rows.capacity());
-
-    for row in rows {
-        fortunes.push(Fortune::from_row(row).unwrap());
-    }
-
-    fortunes.push(Fortune {
-        id: 0,
-        message: "Additional fortune added at request time.".to_string(),
-    });
-
-    fortunes.sort_by(|a, b| a.message.cmp(&b.message));
-
-    Utf8Html(
-        FortunesTemplate {
-            fortunes: &fortunes,
-        }
-        .call()
-        .expect("error rendering template"),
-    )
-}
-
-async fn updates(
-    DatabaseConnection(conn): DatabaseConnection,
-    Query(params): Query<Params>,
-) -> impl IntoResponse {
-    let q = parse_params(params);
-
-    let mut rng = SmallRng::from_entropy();
-
-    let mut results = Vec::with_capacity(q);
-
-    let select = prepare_fetch_world_by_id_statement(&conn).await;
-
-    for _ in 0..q {
-        let query_id = random_number(&mut rng);
-        let mut result: World =
-            fetch_world_by_id_using_statement(&conn, query_id, &select).await;
-
-        result.randomnumber = random_number(&mut rng);
-        results.push(result);
-    }
-
-    let update = prepare_update_world_by_id_statement(&conn).await;
-
-    for w in &results {
-        conn.execute(&update, &[&w.randomnumber, &w.id])
-            .await
-            .unwrap();
-    }
-
-    (StatusCode::OK, Json(results))
-}
-
-async fn prepare_fetch_all_fortunes_statement(conn: &Connection) -> Statement {
-    conn.prepare("SELECT * FROM Fortune").await.unwrap()
-}
-
-async fn prepare_fetch_world_by_id_statement(conn: &Connection) -> Statement {
-    conn.prepare("SELECT id, randomnumber FROM World WHERE id = $1")
-        .await
-        .unwrap()
-}
-
-async fn prepare_update_world_by_id_statement(conn: &Connection) -> Statement {
-    conn.prepare("UPDATE World SET randomnumber = $1 WHERE id = $2")
-        .await
-        .unwrap()
-}
-
-#[tokio::main]
-async fn main() {
-    dotenv().ok();
-
-    let database_url = env::var("AXUM_TECHEMPOWER_DATABASE_URL")
-        .expect("AXUM_TECHEMPOWER_DATABASE_URL environment variable was not set");
-
-    // setup connection pool
-    let pool = create_bb8_pool(database_url).await;
-
-    let router = Router::new()
-        .route("/fortunes", get(fortunes))
-        .route("/db", get(db))
-        .route("/queries", get(queries))
-        .route("/updates", get(updates))
-        .layer(Extension(pool))
-        .layer(SetResponseHeaderLayer::if_not_present(
-            header::SERVER,
-            HeaderValue::from_static("Axum"),
-        ));
-
-    server::builder()
-        .serve(router.into_make_service())
-        .await
-        .unwrap();
-}
-
-#[derive(Template)]
-#[template(path = "fortunes.html.hbs")]
-pub struct FortunesTemplate<'a> {
-    pub fortunes: &'a Vec<Fortune>,
-}

+ 106 - 60
frameworks/Rust/axum/src/main_mongo.rs

@@ -1,4 +1,8 @@
-mod common;
+extern crate dotenv;
+extern crate serde_derive;
+#[macro_use]
+extern crate async_trait;
+
 mod database_mongo;
 mod models_common;
 mod models_mongo;
@@ -11,90 +15,103 @@ use axum::{
     Json, Router,
 };
 use dotenv::dotenv;
-use futures::stream::StreamExt;
-use mongodb::options::ClientOptions;
-use mongodb::{bson::doc, Client, Database};
-use rand::rngs::SmallRng;
-use rand::SeedableRng;
-use std::env;
+use mongodb::options::{ClientOptions, Compressor};
+use mongodb::Client;
+use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng};
 use std::time::Duration;
 use tower_http::set_header::SetResponseHeaderLayer;
 use yarte::Template;
 
+use crate::database_mongo::{
+    fetch_fortunes, find_world_by_id, find_worlds, update_worlds,
+};
+use crate::utils::get_environment_variable;
 use database_mongo::DatabaseConnection;
 use models_mongo::FortuneInfo;
 use models_mongo::{Fortune, World};
-use utils::{parse_params, random_number, Params, Utf8Html};
-
-async fn db(DatabaseConnection(mut db): DatabaseConnection) -> impl IntoResponse {
-    let mut rng = SmallRng::from_entropy();
-    let number = random_number(&mut rng);
+use utils::{parse_params, Params, Utf8Html};
 
-    let world = find_world_by_id(&mut db, number).await;
-
-    (StatusCode::OK, Json(world))
+#[derive(Template)]
+#[template(path = "fortunes.html.hbs")]
+pub struct FortunesTemplate<'a> {
+    pub fortunes: &'a Vec<FortuneInfo>,
 }
 
-async fn find_world_by_id(db: &mut Database, number: i32) -> World {
-    let world_collection = db.collection::<World>("world");
+async fn db(DatabaseConnection(db): DatabaseConnection) -> impl IntoResponse {
+    let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
 
-    let filter = doc! { "id": number as f32 };
+    let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
 
-    let world: World = world_collection
-        .find_one(Some(filter), None)
+    let world = find_world_by_id(db, random_id)
         .await
-        .expect("world could not be found")
-        .unwrap();
-    world
+        .expect("world could not be found");
+
+    (StatusCode::OK, Json(world))
 }
 
 async fn queries(
-    DatabaseConnection(mut db): DatabaseConnection,
+    DatabaseConnection(db): DatabaseConnection,
     Query(params): Query<Params>,
 ) -> impl IntoResponse {
     let q = parse_params(params);
 
-    let mut rng = SmallRng::from_entropy();
-
-    let mut results = Vec::with_capacity(q);
+    let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+    let mut ids: Vec<i32> = Vec::with_capacity(q as usize);
 
     for _ in 0..q {
-        let query_id = random_number(&mut rng);
-
-        let result: World = find_world_by_id(&mut db, query_id).await;
+        let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
 
-        results.push(result);
+        ids.push(random_id);
     }
 
+    let worlds = find_worlds(db, ids).await;
+    let results = worlds.expect("worlds could not be retrieved");
+
     (StatusCode::OK, Json(results))
 }
 
-async fn fortunes(DatabaseConnection(db): DatabaseConnection) -> impl IntoResponse {
-    let fortune_collection = db.collection::<Fortune>("fortune");
+async fn updates(
+    DatabaseConnection(db): DatabaseConnection,
+    Query(params): Query<Params>,
+) -> impl IntoResponse {
+    let q = parse_params(params);
+
+    let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+    let mut ids: Vec<i32> = Vec::with_capacity(q as usize);
+
+    for _ in 0..q {
+        let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
 
-    let mut fortune_cursor = fortune_collection
-        .find(None, None)
+        ids.push(random_id);
+    }
+
+    let worlds = find_worlds(db.clone(), ids)
         .await
-        .expect("fortunes could not be loaded");
+        .expect("worlds could not be retrieved");
+    let mut updated_worlds: Vec<World> = Vec::with_capacity(q as usize);
 
-    let mut fortunes: Vec<Fortune> = Vec::with_capacity(100);
+    for mut world in worlds {
+        let random_number = (rng.gen::<u32>() % 10_000 + 1) as i32;
 
-    while let Some(doc) = fortune_cursor.next().await {
-        fortunes.push(doc.expect("could not load fortune"));
+        world.random_number = random_number as f32;
+        updated_worlds.push(world);
     }
 
-    fortunes.push(Fortune {
-        id: 0.0,
-        message: "Additional fortune added at request time.".to_string(),
-    });
+    update_worlds(db.clone(), updated_worlds.clone())
+        .await
+        .expect("could not update worlds");
+
+    (StatusCode::OK, Json(updated_worlds.clone()))
+}
 
-    fortunes.sort_by(|a, b| a.message.cmp(&b.message));
+async fn fortunes(DatabaseConnection(db): DatabaseConnection) -> impl IntoResponse {
+    let fortunes = fetch_fortunes(db).await.expect("could not fetch fortunes");
 
     let fortune_infos: Vec<FortuneInfo> = fortunes
-        .into_iter()
+        .iter()
         .map(|f| FortuneInfo {
             id: f.id as i32,
-            message: f.message,
+            message: f.message.clone(),
         })
         .collect();
 
@@ -107,29 +124,62 @@ async fn fortunes(DatabaseConnection(db): DatabaseConnection) -> impl IntoRespon
     )
 }
 
-#[tokio::main]
-async fn main() {
+fn main() {
     dotenv().ok();
 
-    let database_url = env::var("AXUM_TECHEMPOWER_MONGODB_URL")
-        .expect("AXUM_TECHEMPOWER_MONGODB_URL environment variable was not set");
+    let rt = tokio::runtime::Builder::new_current_thread()
+        .enable_all()
+        .build()
+        .unwrap();
+
+    for _ in 1..num_cpus::get() {
+        std::thread::spawn(move || {
+            let rt = tokio::runtime::Builder::new_current_thread()
+                .enable_all()
+                .build()
+                .unwrap();
+            rt.block_on(serve());
+        });
+    }
+    rt.block_on(serve());
+}
+
+async fn serve() {
+    let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_MONGODB_URL");
+    let max_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MAX_POOL_SIZE");
+    let min_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MIN_POOL_SIZE");
 
-    // setup connection pool
     let mut client_options = ClientOptions::parse(database_url).await.unwrap();
-    client_options.max_pool_size = Some(common::POOL_SIZE);
-    client_options.min_pool_size = Some(common::POOL_SIZE);
+
+    // setup connection pool
+    client_options.max_pool_size = Some(max_pool_size);
+    client_options.min_pool_size = Some(min_pool_size);
     client_options.connect_timeout = Some(Duration::from_millis(200));
 
+    // the server will select the algorithm it supports from the list provided by the driver
+    client_options.compressors = Some(vec![
+        Compressor::Snappy,
+        Compressor::Zlib {
+            level: Default::default(),
+        },
+        Compressor::Zstd {
+            level: Default::default(),
+        },
+    ]);
+
     let client = Client::with_options(client_options).unwrap();
+    let database = client.database("hello_world");
+    let server_header_value = HeaderValue::from_static("Axum");
 
     let app = Router::new()
         .route("/fortunes", get(fortunes))
         .route("/db", get(db))
         .route("/queries", get(queries))
-        .layer(Extension(client))
+        .route("/updates", get(updates))
+        .layer(Extension(database))
         .layer(SetResponseHeaderLayer::if_not_present(
             header::SERVER,
-            HeaderValue::from_static("Axum"),
+            server_header_value,
         ));
 
     server::builder()
@@ -138,8 +188,4 @@ async fn main() {
         .unwrap();
 }
 
-#[derive(Template)]
-#[template(path = "fortunes.html.hbs")]
-pub struct FortunesTemplate<'a> {
-    pub fortunes: &'a Vec<FortuneInfo>,
-}
+

+ 162 - 0
frameworks/Rust/axum/src/main_mongo_raw.rs

@@ -0,0 +1,162 @@
+extern crate dotenv;
+extern crate serde_derive;
+#[macro_use]
+extern crate async_trait;
+
+mod database_mongo_raw;
+mod models_common;
+mod models_mongo;
+mod server;
+mod utils;
+
+use axum::http::{header, HeaderValue};
+use axum::{
+    extract::Query, http::StatusCode, response::IntoResponse, routing::get, Extension,
+    Json, Router,
+};
+use dotenv::dotenv;
+use mongodb::options::{ClientOptions, Compressor};
+use mongodb::Client;
+use rand::{rngs::SmallRng, thread_rng, Rng, SeedableRng};
+use std::time::Duration;
+use tower_http::set_header::SetResponseHeaderLayer;
+
+use database_mongo_raw::{
+    find_world_by_id, find_worlds, update_worlds,
+};
+use utils::get_environment_variable;
+use database_mongo_raw::DatabaseConnection;
+use models_mongo::{World};
+use utils::{parse_params, Params};
+
+async fn db(DatabaseConnection(db): DatabaseConnection) -> impl IntoResponse {
+    let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+
+    let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+
+    let world = find_world_by_id(db, random_id)
+        .await
+        .expect("world could not be found");
+
+    (StatusCode::OK, Json(world))
+}
+
+async fn queries(
+    DatabaseConnection(db): DatabaseConnection,
+    Query(params): Query<Params>,
+) -> impl IntoResponse {
+    let q = parse_params(params);
+
+    let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+    let mut ids: Vec<i32> = Vec::with_capacity(q as usize);
+
+    for _ in 0..q {
+        let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+
+        ids.push(random_id);
+    }
+
+    let worlds = find_worlds(db, ids).await;
+    let results = worlds.expect("worlds could not be retrieved");
+
+    (StatusCode::OK, Json(results))
+}
+
+async fn updates(
+    DatabaseConnection(db): DatabaseConnection,
+    Query(params): Query<Params>,
+) -> impl IntoResponse {
+    let q = parse_params(params);
+
+    let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+    let mut ids: Vec<i32> = Vec::with_capacity(q as usize);
+
+    for _ in 0..q {
+        let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+
+        ids.push(random_id);
+    }
+
+    let worlds = find_worlds(db.clone(), ids)
+        .await
+        .expect("worlds could not be retrieved");
+    let mut updated_worlds: Vec<World> = Vec::with_capacity(q as usize);
+
+    for mut world in worlds {
+        let random_number = (rng.gen::<u32>() % 10_000 + 1) as i32;
+
+        world.random_number = random_number as f32;
+        updated_worlds.push(world);
+    }
+
+    update_worlds(db.clone(), updated_worlds.clone())
+        .await
+        .expect("could not update worlds");
+
+    (StatusCode::OK, Json(updated_worlds.clone()))
+}
+
+fn main() {
+    dotenv().ok();
+
+    let rt = tokio::runtime::Builder::new_current_thread()
+        .enable_all()
+        .build()
+        .unwrap();
+
+    for _ in 1..num_cpus::get() {
+        std::thread::spawn(move || {
+            let rt = tokio::runtime::Builder::new_current_thread()
+                .enable_all()
+                .build()
+                .unwrap();
+            rt.block_on(serve());
+        });
+    }
+    rt.block_on(serve());
+}
+
+async fn serve() {
+    let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_MONGODB_URL");
+    let max_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MAX_POOL_SIZE");
+    let min_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MIN_POOL_SIZE");
+
+    let mut client_options = ClientOptions::parse(database_url).await.unwrap();
+
+    // setup connection pool
+    client_options.max_pool_size = Some(max_pool_size);
+    client_options.min_pool_size = Some(min_pool_size);
+    client_options.connect_timeout = Some(Duration::from_millis(200));
+
+    // the server will select the algorithm it supports from the list provided by the driver
+    client_options.compressors = Some(vec![
+        Compressor::Snappy,
+        Compressor::Zlib {
+            level: Default::default(),
+        },
+        Compressor::Zstd {
+            level: Default::default(),
+        },
+    ]);
+
+    let client = Client::with_options(client_options).unwrap();
+    let database = client.database("hello_world");
+    let server_header_value = HeaderValue::from_static("Axum");
+
+    let app = Router::new()
+        .route("/db", get(db))
+        .route("/queries", get(queries))
+        .route("/updates", get(updates))
+        .layer(Extension(database))
+        .layer(SetResponseHeaderLayer::if_not_present(
+            header::SERVER,
+            server_header_value,
+        ));
+
+    server::builder()
+        .serve(app.into_make_service())
+        .await
+        .unwrap();
+}
+
+

+ 119 - 0
frameworks/Rust/axum/src/main_pg.rs

@@ -0,0 +1,119 @@
+extern crate dotenv;
+extern crate serde_derive;
+#[macro_use]
+extern crate async_trait;
+
+mod database_pg;
+mod models_common;
+mod models_pg;
+mod server;
+mod utils;
+
+use axum::http::{header, HeaderValue};
+use axum::{
+    extract::Query, http::StatusCode, response::IntoResponse, routing::get, Extension,
+    Json, Router,
+};
+use dotenv::dotenv;
+use tower_http::set_header::SetResponseHeaderLayer;
+use yarte::Template;
+
+use crate::database_pg::{DatabaseConnection, PgConnection};
+use models_pg::Fortune;
+use utils::{parse_params, Params};
+
+use crate::utils::{get_environment_variable, Utf8Html};
+
+#[derive(Template)]
+#[template(path = "fortunes.html.hbs")]
+pub struct FortunesTemplate<'a> {
+    pub fortunes: &'a Vec<Fortune>,
+}
+
+async fn db(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse {
+    let world = conn.get_world().await.expect("error loading world");
+
+    (StatusCode::OK, Json(world))
+}
+
+async fn queries(
+    DatabaseConnection(conn): DatabaseConnection,
+    Query(params): Query<Params>,
+) -> impl IntoResponse {
+    let q = parse_params(params);
+
+    let results = conn
+        .get_worlds(q as usize)
+        .await
+        .expect("error loading worlds");
+
+    (StatusCode::OK, Json(results))
+}
+
+async fn fortunes(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse {
+    let fortunes: Vec<Fortune> =
+        conn.tell_fortune().await.expect("error loading fortunes");
+
+    Utf8Html(
+        FortunesTemplate {
+            fortunes: &fortunes,
+        }
+        .call()
+        .expect("error rendering template"),
+    )
+}
+
+async fn updates(
+    DatabaseConnection(conn): DatabaseConnection,
+    Query(params): Query<Params>,
+) -> impl IntoResponse {
+    let q = parse_params(params);
+
+    let results = conn.update(q as u16).await.expect("error updating worlds");
+
+    (StatusCode::OK, Json(results))
+}
+
+fn main() {
+    dotenv().ok();
+
+    let rt = tokio::runtime::Builder::new_current_thread()
+        .enable_all()
+        .build()
+        .unwrap();
+
+    for _ in 1..num_cpus::get() {
+        std::thread::spawn(move || {
+            let rt = tokio::runtime::Builder::new_current_thread()
+                .enable_all()
+                .build()
+                .unwrap();
+            rt.block_on(serve());
+        });
+    }
+    rt.block_on(serve());
+}
+
+async fn serve() {
+    let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_DATABASE_URL");
+
+    // setup connection pool
+    let pg_connection = PgConnection::connect(database_url).await;
+    let server_header_value = HeaderValue::from_static("Axum");
+
+    let router = Router::new()
+        .route("/fortunes", get(fortunes))
+        .route("/db", get(db))
+        .route("/queries", get(queries))
+        .route("/updates", get(updates))
+        .layer(Extension(pg_connection.clone()))
+        .layer(SetResponseHeaderLayer::if_not_present(
+            header::SERVER,
+            server_header_value,
+        ));
+
+    server::builder()
+        .serve(router.into_make_service())
+        .await
+        .unwrap();
+}

+ 171 - 0
frameworks/Rust/axum/src/main_pg_pool.rs

@@ -0,0 +1,171 @@
+extern crate dotenv;
+extern crate serde_derive;
+#[macro_use]
+extern crate async_trait;
+
+mod database_pg_pool;
+mod models_common;
+mod models_pg_pool;
+mod server;
+mod utils;
+
+use crate::database_pg_pool::{
+    create_pool, fetch_all_fortunes, fetch_world_by_id,
+    prepare_fetch_all_fortunes_statement, prepare_fetch_world_by_id_statement,
+    prepare_update_world_by_id_statement, update_world, DatabaseClient, PgError,
+};
+use axum::http::{header, HeaderValue};
+use axum::{
+    extract::Query, http::StatusCode, response::IntoResponse, routing::get, Extension,
+    Json, Router,
+};
+use dotenv::dotenv;
+use futures_util::stream::FuturesUnordered;
+use futures_util::TryStreamExt;
+use rand::rngs::SmallRng;
+use rand::{thread_rng, Rng, SeedableRng};
+use tower_http::set_header::SetResponseHeaderLayer;
+use yarte::Template;
+
+use crate::utils::{get_environment_variable, Utf8Html};
+use models_pg_pool::{Fortune, World};
+use utils::{parse_params, random_number, Params};
+
+#[derive(Template)]
+#[template(path = "fortunes.html.hbs")]
+pub struct FortunesTemplate<'a> {
+    pub fortunes: &'a Vec<Fortune>,
+}
+
+async fn db(DatabaseClient(client): DatabaseClient) -> impl IntoResponse {
+    let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+
+    let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+
+    let select = prepare_fetch_world_by_id_statement(&client).await;
+    let world = fetch_world_by_id(&client, random_id, &select)
+        .await
+        .expect("could not fetch world");
+
+    (StatusCode::OK, Json(world))
+}
+
+async fn queries(
+    DatabaseClient(client): DatabaseClient,
+    Query(params): Query<Params>,
+) -> impl IntoResponse {
+    let q = parse_params(params);
+
+    let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
+
+    let select = prepare_fetch_world_by_id_statement(&client).await;
+
+    let future_worlds = FuturesUnordered::new();
+
+    for _ in 0..q {
+        let w_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
+
+        future_worlds.push(fetch_world_by_id(&client, w_id, &select));
+    }
+
+    let worlds: Result<Vec<World>, PgError> = future_worlds.try_collect().await;
+    let results = worlds.expect("worlds could not be retrieved");
+
+    (StatusCode::OK, Json(results))
+}
+
+async fn fortunes(DatabaseClient(client): DatabaseClient) -> impl IntoResponse {
+    let select = prepare_fetch_all_fortunes_statement(&client).await;
+
+    let mut fortunes = fetch_all_fortunes(client, &select)
+        .await
+        .expect("could not fetch fortunes");
+
+    fortunes.push(Fortune {
+        id: 0,
+        message: "Additional fortune added at request time.".to_string(),
+    });
+
+    fortunes.sort_by(|a, b| a.message.cmp(&b.message));
+
+    Utf8Html(
+        FortunesTemplate {
+            fortunes: &fortunes,
+        }
+        .call()
+        .expect("error rendering template"),
+    )
+}
+
+async fn updates(
+    DatabaseClient(client): DatabaseClient,
+    Query(params): Query<Params>,
+) -> impl IntoResponse {
+    let q = parse_params(params);
+
+    let mut rng = SmallRng::from_entropy();
+
+    let select = prepare_fetch_world_by_id_statement(&client).await;
+
+    let future_worlds = FuturesUnordered::new();
+
+    for _ in 0..q {
+        let query_id = random_number(&mut rng);
+
+        future_worlds.push(fetch_world_by_id(&client, query_id, &select));
+    }
+
+    let worlds: Result<Vec<World>, PgError> = future_worlds.try_collect().await;
+    let results = worlds.expect("worlds could not be retrieved");
+
+    let update = prepare_update_world_by_id_statement(&client).await;
+
+    let future_world_updates = FuturesUnordered::new();
+
+    for w in &results {
+        let random_id = random_number(&mut rng);
+        let w_id = w.id;
+
+        future_world_updates.push(update_world(&client, &update, random_id, w_id));
+    }
+
+    let world_updates: Result<Vec<u64>, PgError> =
+        future_world_updates.try_collect().await;
+    world_updates.expect("updates could not be executed");
+
+    (StatusCode::OK, Json(results))
+}
+
+#[tokio::main]
+async fn main() {
+    dotenv().ok();
+
+    serve().await;
+}
+
+async fn serve() {
+    let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_DATABASE_URL");
+    let max_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MAX_POOL_SIZE");
+
+    // setup Client pool
+    let pool = create_pool(database_url, max_pool_size).await;
+    let server_header_value = HeaderValue::from_static("Axum");
+
+    let router = Router::new()
+        .route("/fortunes", get(fortunes))
+        .route("/db", get(db))
+        .route("/queries", get(queries))
+        .route("/updates", get(updates))
+        .layer(Extension(pool))
+        .layer(SetResponseHeaderLayer::if_not_present(
+            header::SERVER,
+            server_header_value,
+        ));
+
+    server::builder()
+        .serve(router.into_make_service())
+        .await
+        .unwrap();
+}
+
+

+ 34 - 93
frameworks/Rust/axum/src/main_sqlx.rs

@@ -1,74 +1,54 @@
-mod common;
+extern crate dotenv;
+extern crate serde_derive;
+#[macro_use]
+extern crate async_trait;
+
 mod database_sqlx;
 mod models_common;
 mod models_sqlx;
 mod server;
 mod utils;
 
-use crate::database_sqlx::DatabaseConnection;
+use crate::database_sqlx::{fetch_fortunes, fetch_world, DatabaseConnection};
 use axum::http::{header, HeaderValue};
 use axum::{
-    extract::Query, http::StatusCode, response::IntoResponse, routing::get, Extension,
-    Json, Router,
+    extract::Extension, http::StatusCode, response::IntoResponse, routing::get, Json,
+    Router,
 };
 use dotenv::dotenv;
 use rand::rngs::SmallRng;
-use rand::SeedableRng;
+use rand::{thread_rng, Rng, SeedableRng};
 use sqlx::PgPool;
-use std::env;
 use tower_http::set_header::SetResponseHeaderLayer;
 use yarte::Template;
 
+use crate::utils::get_environment_variable;
 use database_sqlx::create_pool;
 use models_sqlx::{Fortune, World};
-use utils::{parse_params, random_number, Params, Utf8Html};
-
-async fn db(DatabaseConnection(mut conn): DatabaseConnection) -> impl IntoResponse {
-    let mut rng = SmallRng::from_entropy();
-    let number = random_number(&mut rng);
-
-    let world: World =
-        sqlx::query_as("SELECT id, randomnumber FROM World WHERE id = $1")
-            .bind(number)
-            .fetch_one(&mut conn)
-            .await
-            .expect("error loading world");
+use utils::Utf8Html;
 
-    (StatusCode::OK, Json(world))
+#[derive(Template)]
+#[template(path = "fortunes.html.hbs")]
+pub struct FortunesTemplate<'a> {
+    pub fortunes: &'a Vec<Fortune>,
 }
 
-async fn queries(
-    DatabaseConnection(mut conn): DatabaseConnection,
-    Query(params): Query<Params>,
-) -> impl IntoResponse {
-    let q = parse_params(params);
-
-    let mut rng = SmallRng::from_entropy();
+async fn db(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse {
+    let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
 
-    let mut results = Vec::with_capacity(q);
+    let random_id = (rng.gen::<u32>() % 10_000 + 1) as i32;
 
-    for _ in 0..q {
-        let query_id = random_number(&mut rng);
-
-        let result: World = sqlx::query_as("SELECT * FROM World WHERE id = $1")
-            .bind(query_id)
-            .fetch_one(&mut conn)
-            .await
-            .expect("error loading world");
-
-        results.push(result);
-    }
+    let world = fetch_world(conn, random_id)
+        .await
+        .expect("could not fetch world");
 
-    (StatusCode::OK, Json(results))
+    (StatusCode::OK, Json(world))
 }
 
-async fn fortunes(
-    DatabaseConnection(mut conn): DatabaseConnection,
-) -> impl IntoResponse {
-    let mut fortunes: Vec<Fortune> = sqlx::query_as("SELECT * FROM Fortune")
-        .fetch_all(&mut conn)
+async fn fortunes(DatabaseConnection(conn): DatabaseConnection) -> impl IntoResponse {
+    let mut fortunes = fetch_fortunes(conn)
         .await
-        .expect("Could not load Fortunes");
+        .expect("could not fetch fortunes");
 
     fortunes.push(Fortune {
         id: 0,
@@ -86,49 +66,16 @@ async fn fortunes(
     )
 }
 
-async fn updates(
-    DatabaseConnection(mut conn): DatabaseConnection,
-    Query(params): Query<Params>,
-) -> impl IntoResponse {
-    let q = parse_params(params);
-
-    let mut rng = SmallRng::from_entropy();
-
-    let mut results = Vec::with_capacity(q);
-
-    for _ in 0..q {
-        let query_id = random_number(&mut rng);
-        let mut result: World = sqlx::query_as("SELECT * FROM World WHERE id = $1")
-            .bind(query_id)
-            .fetch_one(&mut conn)
-            .await
-            .expect("error loading world");
-
-        result.random_number = random_number(&mut rng);
-        results.push(result);
-    }
-
-    for w in &results {
-        sqlx::query("UPDATE World SET randomnumber = $1 WHERE id = $2")
-            .bind(w.random_number)
-            .bind(w.id)
-            .execute(&mut conn)
-            .await
-            .expect("could not update world");
-    }
-
-    (StatusCode::OK, Json(results))
-}
-
 #[tokio::main]
 async fn main() {
     dotenv().ok();
 
-    let database_url = env::var("AXUM_TECHEMPOWER_DATABASE_URL")
-        .expect("AXUM_TECHEMPOWER_DATABASE_URL environment variable was not set");
+    let database_url: String = get_environment_variable("AXUM_TECHEMPOWER_DATABASE_URL");
+    let max_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MAX_POOL_SIZE");
+    let min_pool_size: u32 = get_environment_variable("AXUM_TECHEMPOWER_MIN_POOL_SIZE");
 
     // setup connection pool
-    let pool = create_pool(database_url).await;
+    let pool = create_pool(database_url, max_pool_size, min_pool_size).await;
 
     let app = router(pool).await;
 
@@ -139,20 +86,14 @@ async fn main() {
 }
 
 async fn router(pool: PgPool) -> Router {
+    let server_header_value = HeaderValue::from_static("Axum");
+
     Router::new()
         .route("/fortunes", get(fortunes))
         .route("/db", get(db))
-        .route("/queries", get(queries))
-        .route("/updates", get(updates))
         .layer(Extension(pool))
         .layer(SetResponseHeaderLayer::if_not_present(
             header::SERVER,
-            HeaderValue::from_static("Axum"),
-        ))
-}
-
-#[derive(Template)]
-#[template(path = "fortunes.html.hbs")]
-pub struct FortunesTemplate<'a> {
-    pub fortunes: &'a Vec<Fortune>,
-}
+            server_header_value)
+        )
+}

+ 2 - 2
frameworks/Rust/axum/src/models_common.rs

@@ -1,6 +1,6 @@
-use serde::{Serialize};
+use serde::Serialize;
 
 #[derive(Serialize)]
 pub struct Message {
     pub message: &'static str,
-}
+}

+ 3 - 6
frameworks/Rust/axum/src/models_mongo.rs

@@ -3,21 +3,18 @@ use serde::{Deserialize, Serialize};
 #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
 pub struct Fortune {
     pub id: f32,
-    pub message: String
+    pub message: String,
 }
 
 #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
 pub struct FortuneInfo {
     pub id: i32,
-    pub message: String
+    pub message: String,
 }
 
 #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
 pub struct World {
     pub id: f32,
     #[serde(rename = "randomNumber")]
-    pub random_number: f32
+    pub random_number: f32,
 }
-
-
-

+ 16 - 0
frameworks/Rust/axum/src/models_pg.rs

@@ -0,0 +1,16 @@
+use serde::{Deserialize, Serialize};
+
+#[allow(non_snake_case)]
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub struct Fortune {
+    pub id: i32,
+    pub message: String,
+}
+
+#[allow(non_snake_case)]
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
+pub struct World {
+    pub id: i32,
+    #[serde(rename = "randomNumber")]
+    pub randomnumber: i32,
+}

+ 2 - 5
frameworks/Rust/axum/src/models_bb8.rs → frameworks/Rust/axum/src/models_pg_pool.rs

@@ -6,7 +6,7 @@ use tokio_pg_mapper_derive::PostgresMapper;
 #[pg_mapper(table = "Fortune")]
 pub struct Fortune {
     pub id: i32,
-    pub message: String
+    pub message: String,
 }
 
 #[allow(non_snake_case)]
@@ -15,8 +15,5 @@ pub struct Fortune {
 pub struct World {
     pub id: i32,
     #[serde(rename = "randomNumber")]
-    pub randomnumber: i32
+    pub randomnumber: i32,
 }
-
-
-

+ 2 - 5
frameworks/Rust/axum/src/models_sqlx.rs

@@ -4,7 +4,7 @@ use sqlx::FromRow;
 #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, FromRow)]
 pub struct Fortune {
     pub id: i32,
-    pub message: String
+    pub message: String,
 }
 
 #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, FromRow)]
@@ -12,8 +12,5 @@ pub struct World {
     pub id: i32,
     #[sqlx(rename = "randomnumber")]
     #[serde(rename = "randomNumber")]
-    pub random_number: i32
+    pub random_number: i32,
 }
-
-
-

+ 9 - 9
frameworks/Rust/axum/src/server.rs

@@ -1,19 +1,19 @@
-
 use std::io;
 use std::net::{Ipv4Addr, SocketAddr};
 
 use hyper::server::conn::AddrIncoming;
 use tokio::net::{TcpListener, TcpSocket};
-use crate::common::POOL_SIZE;
 
 pub fn builder() -> hyper::server::Builder<AddrIncoming> {
     let addr = SocketAddr::from((Ipv4Addr::UNSPECIFIED, 8000));
     let listener = reuse_listener(addr).expect("couldn't bind to addr");
     let incoming = AddrIncoming::from_listener(listener).unwrap();
 
-    println!("Started axum server at 8000 with pool size {pool_size}", pool_size=POOL_SIZE);
+    println!("Started axum server at 8000");
 
-    axum::Server::builder(incoming).http1_only(true).tcp_nodelay(true)
+    axum::Server::builder(incoming)
+        .http1_only(true)
+        .tcp_nodelay(true)
 }
 
 fn reuse_listener(addr: SocketAddr) -> io::Result<TcpListener> {
@@ -23,13 +23,13 @@ fn reuse_listener(addr: SocketAddr) -> io::Result<TcpListener> {
     };
 
     #[cfg(unix)]
-        {
-            if let Err(e) = socket.set_reuseport(true) {
-                eprintln!("error setting SO_REUSEPORT: {}", e);
-            }
+    {
+        if let Err(e) = socket.set_reuseport(true) {
+            eprintln!("error setting SO_REUSEPORT: {}", e);
         }
+    }
 
     socket.set_reuseaddr(true)?;
     socket.bind(addr)?;
     socket.listen(1024)
-}
+}

+ 45 - 15
frameworks/Rust/axum/src/utils.rs

@@ -1,31 +1,61 @@
+use axum::body::{Bytes, Full};
 use axum::http::{header, HeaderValue, StatusCode};
-use axum::response::IntoResponse;
-use axum::{
-    body::{Bytes, Full},
-    response::Response,
-};
+use axum_core::response::IntoResponse;
+use axum_core::response::Response;
 use rand::rngs::SmallRng;
 use rand::Rng;
 use serde::Deserialize;
 
+use std::env;
+use std::fmt::Debug;
+use std::str::FromStr;
+
+pub fn get_environment_variable<T: FromStr>(key: &str) -> T
+where
+    <T as FromStr>::Err: Debug,
+{
+    T::from_str(
+        &*env::var(key)
+            .ok()
+            .expect(&*format!("{} environment variable was not set", key)),
+    )
+    .expect(&*format!("could not parse {}", key))
+}
+
 #[derive(Debug, Deserialize)]
 pub struct Params {
     queries: Option<String>,
 }
 
+#[allow(dead_code)]
 pub fn random_number(rng: &mut SmallRng) -> i32 {
     (rng.gen::<u32>() % 10_000 + 1) as i32
 }
 
-pub fn parse_params(params: Params) -> usize {
-    num::clamp(
-        params
-            .queries
-            .map(|queries| queries.parse::<i32>().unwrap_or(1))
-            .unwrap_or(0),
-        1,
-        500,
-    ) as usize
+#[allow(dead_code)]
+pub fn parse_params(params: Params) -> i32 {
+    let mut q = 0;
+
+    if params.queries.is_some() {
+        let queries = params.queries.ok_or("could not get value").unwrap();
+
+        let queries_as_int = queries.parse::<i32>();
+
+        match queries_as_int {
+            Ok(_ok) => q = queries_as_int.unwrap(),
+            Err(_e) => q = 1,
+        }
+    }
+
+    let q = if q == 0 {
+        1
+    } else if q > 500 {
+        500
+    } else {
+        q
+    };
+
+    q
 }
 
 /// Utility function for mapping any error into a `500 Internal Server Error`
@@ -45,7 +75,7 @@ where
     T: Into<Full<Bytes>>,
 {
     fn into_response(self) -> Response {
-        let mut res = self.0.into().into_response();
+        let mut res = (StatusCode::OK, self.0.into()).into_response();
         res.headers_mut().insert(
             header::CONTENT_TYPE,
             HeaderValue::from_static("text/html; charset=utf-8"),