Browse Source

[xitca-web] bug fix and improvements (#9272)

* [xitca-web] bug fix and improvements

* comment fix
fakeshadow 11 months ago
parent
commit
45c151d5ab

+ 31 - 38
frameworks/Rust/xitca-web/Cargo.lock

@@ -4,18 +4,18 @@ version = 3
 
 [[package]]
 name = "addr2line"
-version = "0.22.0"
+version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
+checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375"
 dependencies = [
  "gimli",
 ]
 
 [[package]]
-name = "adler"
-version = "1.0.2"
+name = "adler2"
+version = "2.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
 
 [[package]]
 name = "async-trait"
@@ -95,17 +95,17 @@ dependencies = [
 
 [[package]]
 name = "backtrace"
-version = "0.3.73"
+version = "0.3.74"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
 dependencies = [
  "addr2line",
- "cc",
  "cfg-if",
  "libc",
  "miniz_oxide",
  "object",
  "rustc-demangle",
+ "windows-targets",
 ]
 
 [[package]]
@@ -149,9 +149,9 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
 
 [[package]]
 name = "cc"
-version = "1.1.16"
+version = "1.1.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b"
+checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476"
 dependencies = [
  "shlex",
 ]
@@ -164,9 +164,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.13"
+version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
+checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
 dependencies = [
  "libc",
 ]
@@ -364,9 +364,9 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.29.0"
+version = "0.31.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64"
 
 [[package]]
 name = "heck"
@@ -554,11 +554,11 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.7.4"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
+checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
 dependencies = [
- "adler",
+ "adler2",
 ]
 
 [[package]]
@@ -572,12 +572,6 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "nanorand"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
-
 [[package]]
 name = "num-traits"
 version = "0.2.19"
@@ -721,9 +715,9 @@ dependencies = [
 
 [[package]]
 name = "pq-sys"
-version = "0.6.1"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a24ff9e4cf6945c988f0db7005d87747bf72864965c3529d259ad155ac41d584"
+checksum = "a92c30dd81695321846d4dfe348da67b1752ebb61cd1549d203a7b57e323c435"
 dependencies = [
  "vcpkg",
 ]
@@ -789,9 +783,9 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.3"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
+checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
 dependencies = [
  "bitflags 2.6.0",
 ]
@@ -867,18 +861,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
 name = "serde"
-version = "1.0.209"
+version = "1.0.210"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
+checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.209"
+version = "1.0.210"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
+checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1155,9 +1149,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.12"
+version = "1.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
 
 [[package]]
 name = "unicode-normalization"
@@ -1330,9 +1324,9 @@ dependencies = [
 
 [[package]]
 name = "xitca-io"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "376a4849eef1f3475a6b7a5c474dac278c320a28f6e2abc07be57048b35cc5df"
+checksum = "19b91b7a5ff9e3bed167b7e3bcc7b4462d2cb16d05e3ae913dbc384e463fdd7f"
 dependencies = [
  "bytes",
  "tokio",
@@ -1343,7 +1337,7 @@ dependencies = [
 [[package]]
 name = "xitca-postgres"
 version = "0.1.0"
-source = "git+https://github.com/HFQR/xitca-web.git?rev=9835c0b#9835c0b79ebd77f01b74e3ccb1c9893f022db9f3"
+source = "git+https://github.com/HFQR/xitca-web.git?rev=0cda225#0cda2254f98b40f21bc3170dd8983f16444f0bd0"
 dependencies = [
  "fallible-iterator",
  "percent-encoding",
@@ -1353,7 +1347,6 @@ dependencies = [
  "tokio",
  "tracing",
  "xitca-io",
- "xitca-service",
  "xitca-unsafe-collection",
 ]
 
@@ -1406,7 +1399,7 @@ dependencies = [
  "futures-core",
  "http-body",
  "mimalloc",
- "nanorand",
+ "rand",
  "sailfish",
  "serde",
  "serde_json",

+ 2 - 2
frameworks/Rust/xitca-web/Cargo.toml

@@ -85,7 +85,7 @@ mimalloc = { version = "0.1", default-features = false, optional = true }
 # stuff can not be used or not needed in wasi target
 [target.'cfg(not(target_family = "wasm"))'.dependencies]
 futures-core = { version = "0.3", default-features = false }
-nanorand = { version = "0.7", default-features = false, features = ["tls"] }
+rand = { version = "0.8", features = ["small_rng"] }
 tokio = "1"
 
 [profile.release]
@@ -95,5 +95,5 @@ codegen-units = 1
 panic = "abort"
 
 [patch.crates-io]
-xitca-postgres = { git = "https://github.com/HFQR/xitca-web.git", rev = "9835c0b" }
+xitca-postgres = { git = "https://github.com/HFQR/xitca-web.git", rev = "0cda225" }
 mio = { git = "https://github.com/fakeshadow/mio", rev = "9bae6012b7ecfc6083350785f71a5e8265358178" }

+ 16 - 27
frameworks/Rust/xitca-web/src/db.rs

@@ -1,11 +1,12 @@
-use std::fmt::Write;
+// clippy is dumb and have no idea what should be lazy or not
+#![allow(clippy::unnecessary_lazy_evaluations)]
 
 use xitca_io::bytes::BytesMut;
-use xitca_postgres::{pipeline::Pipeline, AsyncLendingIterator, Pool, Type};
+use xitca_postgres::{pipeline::Pipeline, pool::Pool, AsyncLendingIterator, Type};
 
 use super::{
     ser::{Fortune, Fortunes, World},
-    util::{HandleResult, Rand, DB_URL},
+    util::{bulk_update_gen, HandleResult, Rand, DB_URL},
 };
 
 pub struct Client {
@@ -28,19 +29,14 @@ const WORLD_SQL: &str = "SELECT * FROM world WHERE id=$1";
 const WORLD_SQL_TYPES: &[Type] = &[Type::INT4];
 
 fn update_query(num: usize) -> Box<str> {
-    const PREFIX: &str = "UPDATE world SET randomNumber = w.r FROM (VALUES ";
-    const SUFFIX: &str = ") AS w (i,r) WHERE world.id = w.i";
-
-    let (_, mut query) = (1..=num).fold((1, String::from(PREFIX)), |(idx, mut query), _| {
-        write!(query, "(${}::int,${}::int),", idx, idx + 1).unwrap();
-        (idx + 2, query)
-    });
-
-    query.pop();
-
-    query.push_str(SUFFIX);
-
-    query.into_boxed_str()
+    bulk_update_gen(|query| {
+        use std::fmt::Write;
+        (1..=num).fold((1, query), |(idx, query), _| {
+            write!(query, "(${}::int,${}::int),", idx, idx + 1).unwrap();
+            (idx + 2, query)
+        });
+    })
+    .into_boxed_str()
 }
 
 pub async fn create() -> HandleResult<Client> {
@@ -50,14 +46,7 @@ pub async fn create() -> HandleResult<Client> {
 
     let updates = core::iter::once(Box::from(""))
         .chain((1..=500).map(update_query))
-        .collect::<Box<[Box<str>]>>();
-
-    {
-        let mut conn = pool.get().await?;
-        for update in updates.iter().skip(1) {
-            conn.prepare(update, &[]).await?;
-        }
-    }
+        .collect();
 
     Ok(Client {
         pool,
@@ -120,7 +109,7 @@ impl Client {
 
         let mut conn = self.pool.get().await?;
         let world_stmt = conn.prepare(WORLD_SQL, WORLD_SQL_TYPES).await?;
-        let update_stmt = conn.prepare(&update, &[]).await?;
+        let update_stmt = conn.prepare(update, &[]).await?;
 
         let mut params = Vec::with_capacity(len);
 
@@ -169,8 +158,8 @@ impl Client {
     }
 }
 
-fn sort_update_params(params: &Vec<[i32; 2]>) -> impl ExactSizeIterator<Item = i32> {
-    let mut params = params.clone();
+fn sort_update_params(params: &[[i32; 2]]) -> impl ExactSizeIterator<Item = i32> {
+    let mut params = params.to_owned();
     params.sort_by(|a, b| a[0].cmp(&b[0]));
 
     struct ParamIter<I>(I);

+ 31 - 18
frameworks/Rust/xitca-web/src/db_diesel.rs

@@ -1,10 +1,13 @@
-use std::sync::{Arc, Mutex};
+use std::{
+    io,
+    sync::{Arc, Mutex},
+};
 
 use diesel::{prelude::*, r2d2};
 
 use crate::{
     ser::{Fortune, Fortunes, World},
-    util::{Error, HandleResult, Rand, DB_URL},
+    util::{bulk_update_gen, Error, HandleResult, Rand, DB_URL},
 };
 
 pub type Pool = Arc<_Pool>;
@@ -14,7 +17,7 @@ pub struct _Pool {
     rng: Mutex<Rand>,
 }
 
-pub fn create() -> std::io::Result<Arc<_Pool>> {
+pub fn create() -> io::Result<Arc<_Pool>> {
     r2d2::Builder::new()
         .max_size(100)
         .min_idle(Some(100))
@@ -22,7 +25,7 @@ pub fn create() -> std::io::Result<Arc<_Pool>> {
         .idle_timeout(None)
         .max_lifetime(None)
         .build(r2d2::ConnectionManager::new(DB_URL))
-        .map_err(std::io::Error::other)
+        .map_err(io::Error::other)
         .map(|pool| {
             Arc::new(_Pool {
                 pool,
@@ -65,33 +68,32 @@ impl _Pool {
     pub fn update(&self, num: u16) -> HandleResult<Vec<World>> {
         use crate::schema::world::dsl::*;
 
+        let mut rngs = {
+            let mut rng = self.rng.lock().unwrap();
+            (0..num).map(|_| (rng.gen_id(), rng.gen_id())).collect::<Vec<_>>()
+        };
+
+        rngs.sort_by(|(a, _), (b, _)| a.cmp(b));
+
         let mut worlds = {
             let mut conn = self.pool.get()?;
-            let worlds = (0..num)
-                .map(|_| {
-                    let mut rng = self.rng.lock().unwrap();
-                    let w_id = rng.gen_id();
-                    let r_id = rng.gen_id();
-                    drop(rng);
+
+            let worlds = rngs
+                .iter()
+                .map(|(w_id, num)| {
                     world
                         .filter(id.eq(w_id))
                         .load::<World>(&mut conn)?
                         .pop()
                         .map(|mut w| {
-                            w.randomnumber = r_id;
+                            w.randomnumber = *num;
                             w
                         })
                         .ok_or_else(not_found)
                 })
                 .collect::<HandleResult<Vec<_>>>()?;
 
-            worlds.iter().try_for_each(|w| {
-                diesel::update(world)
-                    .filter(id.eq(w.id))
-                    .set(randomnumber.eq(w.randomnumber))
-                    .execute(&mut conn)
-                    .map(|_| ())
-            })?;
+            diesel::sql_query(update_query(&rngs)).execute(&mut conn)?;
 
             worlds
         };
@@ -115,3 +117,14 @@ impl _Pool {
         Ok(Fortunes::new(items))
     }
 }
+
+// diesel does not support high level bulk update api. use raw sql to bypass the limitation.
+// relate discussion: https://github.com/diesel-rs/diesel/discussions/2879
+fn update_query(ids: &[(i32, i32)]) -> String {
+    bulk_update_gen(|query| {
+        use std::fmt::Write;
+        ids.iter().for_each(|(w_id, num)| {
+            write!(query, "({}::int,{}::int),", w_id, num).unwrap();
+        });
+    })
+}

+ 10 - 4
frameworks/Rust/xitca-web/src/main.rs

@@ -39,16 +39,22 @@ async fn middleware<S>(service: &S, req: Ctx<'_>) -> Result<Response, core::conv
 where
     S: for<'c> Service<Ctx<'c>, Response = Response, Error = RouterError<util::Error>>,
 {
-    let mut res = service.call(req).await.unwrap_or_else(|e| match e {
+    let mut res = service.call(req).await.unwrap_or_else(error_handler);
+    res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE);
+    Ok(res)
+}
+
+#[cold]
+#[inline(never)]
+fn error_handler(e: RouterError<util::Error>) -> Response {
+    match e {
         RouterError::Match(_) => error_response(StatusCode::NOT_FOUND),
         RouterError::NotAllowed(_) => error_response(StatusCode::METHOD_NOT_ALLOWED),
         RouterError::Service(e) => {
             println!("{e}");
             error_response(StatusCode::INTERNAL_SERVER_ERROR)
         }
-    });
-    res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE);
-    Ok(res)
+    }
 }
 
 async fn plain_text(ctx: Ctx<'_>) -> HandleResult<Response> {

+ 9 - 75
frameworks/Rust/xitca-web/src/main_iou.rs

@@ -8,43 +8,31 @@ mod db;
 mod ser;
 mod util;
 
-use std::{convert::Infallible, fmt, future::poll_fn, io, pin::pin};
+use std::{convert::Infallible, io};
 
-use futures_core::stream::Stream;
 use xitca_http::{
-    date::DateTimeService,
-    h1::proto::context::Context,
-    http::{header::SERVER, StatusCode},
+    body::ResponseBody,
+    http::{self, header::SERVER, StatusCode},
+    HttpServiceBuilder,
 };
-use xitca_io::{
-    bytes::BytesMut,
-    io_uring::BoundedBuf,
-    net::{io_uring::TcpStream as IOUTcpStream, TcpStream},
-};
-use xitca_service::{fn_build, fn_service, middleware::UncheckedReady, Service, ServiceExt};
+use xitca_service::{fn_service, ServiceExt};
 
 use self::{
-    ser::{error_response, IntoResponse, Message, Request, Response},
+    ser::{error_response, IntoResponse, Message, Request},
     util::{context_mw, Ctx, QueryParse, SERVER_HEADER_VALUE},
 };
 
 fn main() -> io::Result<()> {
     let service = fn_service(handler)
         .enclosed(context_mw())
-        .enclosed(fn_build(|res: Result<_, _>| async {
-            res.map(|service| Http1IOU {
-                service,
-                date: DateTimeService::new(),
-            })
-        }))
-        .enclosed(UncheckedReady);
+        .enclosed(HttpServiceBuilder::h1().io_uring());
     xitca_server::Builder::new()
         .bind("xitca-iou", "0.0.0.0:8080", service)?
         .build()
         .wait()
 }
 
-async fn handler(ctx: Ctx<'_, Request<()>>) -> Result<Response, Infallible> {
+async fn handler<B>(ctx: Ctx<'_, Request<B>>) -> Result<http::Response<ResponseBody>, Infallible> {
     let (req, state) = ctx.into_parts();
     let mut res = match req.uri().path() {
         "/plaintext" => req.text_response().unwrap(),
@@ -71,59 +59,5 @@ async fn handler(ctx: Ctx<'_, Request<()>>) -> Result<Response, Infallible> {
         _ => error_response(StatusCode::NOT_FOUND),
     };
     res.headers_mut().insert(SERVER, SERVER_HEADER_VALUE);
-    Ok(res)
-}
-
-struct Http1IOU<S> {
-    service: S,
-    date: DateTimeService,
-}
-
-// runner for http service.
-impl<S> Service<TcpStream> for Http1IOU<S>
-where
-    S: Service<Request<()>, Response = Response>,
-    S::Error: fmt::Debug,
-{
-    type Response = ();
-    type Error = io::Error;
-
-    async fn call(&self, stream: TcpStream) -> Result<Self::Response, Self::Error> {
-        let std = stream.into_std()?;
-        let stream = IOUTcpStream::from_std(std);
-
-        let mut ctx = Context::<_, 8>::new(self.date.get());
-        let mut read_buf = BytesMut::new();
-        let mut write_buf = BytesMut::with_capacity(4096);
-
-        loop {
-            let len = read_buf.len();
-            let rem = read_buf.capacity() - len;
-            if rem < 4096 {
-                read_buf.reserve(4096 - rem);
-            }
-
-            let (res, buf) = stream.read(read_buf.slice(len..)).await;
-            read_buf = buf.into_inner();
-            if res? == 0 {
-                break;
-            }
-
-            while let Some((req, _)) = ctx.decode_head::<{ usize::MAX }>(&mut read_buf).unwrap() {
-                let (parts, body) = self.service.call(req).await.unwrap().into_parts();
-                let mut encoder = ctx.encode_head(parts, &body, &mut write_buf).unwrap();
-                let mut body = pin!(body);
-                let chunk = poll_fn(|cx| body.as_mut().poll_next(cx)).await.unwrap().unwrap();
-                encoder.encode(chunk, &mut write_buf);
-                encoder.encode_eof(&mut write_buf);
-            }
-
-            let (res, b) = stream.write_all(write_buf).await;
-            write_buf = b;
-            write_buf.clear();
-            res?;
-        }
-
-        stream.shutdown(std::net::Shutdown::Both)
-    }
+    Ok(res.map(Into::into))
 }

+ 4 - 8
frameworks/Rust/xitca-web/src/ser.rs

@@ -62,7 +62,7 @@ impl Fortune {
     }
 }
 
-// TODO: use another template engine with faster compile time.(perferably with no proc macro)
+// TODO: use another template engine with faster compile time.(preferably with no proc macro)
 #[cfg_attr(
     feature = "template",
     derive(sailfish::TemplateOnce),
@@ -84,11 +84,11 @@ impl<'de> Deserialize<'de> for Num {
     where
         D: Deserializer<'de>,
     {
-        use core::{cmp, fmt};
+        use core::fmt;
 
         use serde::de::{Error, MapAccess, Visitor};
 
-        const FIELDS: &'static [&'static str] = &["q"];
+        const FIELDS: &[&str] = &["q"];
 
         struct Field;
 
@@ -135,9 +135,7 @@ impl<'de> Deserialize<'de> for Num {
                 V: MapAccess<'de>,
             {
                 map.next_key::<Field>()?.ok_or_else(|| Error::missing_field("q"))?;
-                let q = map.next_value::<u16>().unwrap_or(1);
-                let q = cmp::min(500, cmp::max(1, q));
-                Ok(Num(q))
+                Ok(Num(map.next_value().unwrap_or(1).clamp(1, 500)))
             }
         }
 
@@ -201,8 +199,6 @@ impl<Ext> IntoResponse for Request<Ext> {
     }
 }
 
-#[cold]
-#[inline(never)]
 pub fn error_response(status: StatusCode) -> Response {
     http::Response::builder()
         .status(status)

+ 37 - 13
frameworks/Rust/xitca-web/src/util.rs

@@ -1,6 +1,6 @@
 #![allow(dead_code)]
 
-use core::{cell::RefCell, cmp};
+use core::cell::RefCell;
 
 use xitca_http::{bytes::BytesMut, http::header::HeaderValue};
 
@@ -10,16 +10,34 @@ pub trait QueryParse {
 
 impl QueryParse for Option<&str> {
     fn parse_query(self) -> u16 {
-        let num = self
-            .and_then(|this| {
-                use atoi::FromRadix10;
-                this.find('q')
-                    .map(|pos| u16::from_radix_10(this.split_at(pos + 2).1.as_ref()).0)
-            })
-            .unwrap_or(1);
+        self.and_then(|this| {
+            use atoi::FromRadix10;
+            this.find('q')
+                .map(|pos| u16::from_radix_10(this.split_at(pos + 2).1.as_ref()).0)
+        })
+        .unwrap_or(1)
+        .clamp(1, 500)
+    }
+}
+
+pub fn bulk_update_gen<F>(func: F) -> String
+where
+    F: FnOnce(&mut String),
+{
+    const PREFIX: &str = "UPDATE world SET randomNumber = w.r FROM (VALUES ";
+    const SUFFIX: &str = ") AS w (i,r) WHERE world.id = w.i";
+
+    let mut query = String::from(PREFIX);
+
+    func(&mut query);
 
-        cmp::min(500, cmp::max(1, num))
+    if query.ends_with(',') {
+        query.pop();
     }
+
+    query.push_str(SUFFIX);
+
+    query
 }
 
 #[allow(clippy::declare_interior_mutable_const)]
@@ -38,14 +56,20 @@ pub struct State<DB> {
 
 #[cfg(not(target_arch = "wasm32"))]
 mod non_wasm {
-    #[derive(Default)]
-    pub struct Rand(nanorand::WyRand);
+    use rand::{rngs::SmallRng, Rng, SeedableRng};
+
+    pub struct Rand(SmallRng);
+
+    impl Default for Rand {
+        fn default() -> Self {
+            Self(SmallRng::from_entropy())
+        }
+    }
 
     impl Rand {
         #[inline]
         pub fn gen_id(&mut self) -> i32 {
-            use nanorand::Rng;
-            (self.0.generate::<u32>() % 10_000 + 1) as _
+            self.0.gen_range(1..=10000)
         }
     }
 

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

@@ -1,4 +1,4 @@
-FROM rust:1.79
+FROM rust:1.81
 
 ADD ./ /xitca-web
 WORKDIR /xitca-web

+ 1 - 1
frameworks/Rust/xitca-web/xitca-web-iou.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.79
+FROM rust:1.81
 
 ADD ./ /xitca-web
 WORKDIR /xitca-web

+ 1 - 1
frameworks/Rust/xitca-web/xitca-web-sync.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.79
+FROM rust:1.81
 
 ADD ./ /xitca-web
 WORKDIR /xitca-web

+ 1 - 1
frameworks/Rust/xitca-web/xitca-web-wasm.dockerfile

@@ -1,7 +1,7 @@
 ARG WASMTIME_VERSION=15.0.0
 ARG WASM_TARGET=wasm32-wasip1-threads
 
-FROM rust:1.79 AS compile
+FROM rust:1.81 AS compile
 
 ARG WASMTIME_VERSION
 ARG WASM_TARGET

+ 1 - 1
frameworks/Rust/xitca-web/xitca-web.dockerfile

@@ -1,4 +1,4 @@
-FROM rust:1.79
+FROM rust:1.81
 
 ADD ./ /xitca-web
 WORKDIR /xitca-web