From 9fdcd85a0fc70c09359a0edee17e5d7ecb6618ff Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Fri, 23 Feb 2024 13:43:35 +0500 Subject: [PATCH 001/129] Reconcile with latest main --- .gitignore | 1 + Cargo.lock | 600 ++++++++++++++++++++------------ Cargo.toml | 29 +- src/bin/loadtest.rs | 47 +-- src/consumer/builder.rs | 131 +++++++ src/consumer/health.rs | 78 +++++ src/consumer/mod.rs | 665 ++++++++++-------------------------- src/consumer/registries.rs | 77 +++++ src/consumer/runner.rs | 99 ++++++ src/error.rs | 6 - src/lib.rs | 21 +- src/producer/mod.rs | 151 ++++---- src/proto/batch/cmd.rs | 81 ++--- src/proto/batch/mod.rs | 68 ++-- src/proto/mod.rs | 390 +++++++++------------ src/proto/single/cmd.rs | 171 +++++----- src/proto/single/ent/cmd.rs | 18 +- src/proto/single/mod.rs | 25 +- src/proto/single/resp.rs | 166 ++++----- src/proto/utils.rs | 83 +++++ src/tls.rs | 157 ++++++--- tests/consumer.rs | 142 ++++---- tests/mock/inner.rs | 39 +++ tests/mock/mod.rs | 76 +++-- tests/producer.rs | 33 +- tests/real/community.rs | 231 +++++++------ tests/real/enterprise.rs | 556 ++++++++++++++++-------------- tests/real/main.rs | 1 + tests/real/utils.rs | 25 ++ tests/tls.rs | 65 ++-- 30 files changed, 2390 insertions(+), 1842 deletions(-) create mode 100644 src/consumer/builder.rs create mode 100644 src/consumer/health.rs create mode 100644 src/consumer/registries.rs create mode 100644 src/consumer/runner.rs create mode 100644 src/proto/utils.rs create mode 100644 tests/mock/inner.rs create mode 100644 tests/real/utils.rs diff --git a/.gitignore b/.gitignore index e8da9172..67b3c5c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target/ **/*.rs.bk perf.* +.vscode \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 07d857dc..d1488183 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -52,7 +67,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -62,26 +77,62 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "async-stream" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] [[package]] -name = "bitflags" -version = "1.3.2" +name = "async-trait" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] [[package]] -name = "bitflags" -version = "2.4.2" +name = "autocfg" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "block-buffer" @@ -93,25 +144,22 @@ dependencies = [ ] [[package]] -name = "bufstream" -version = "0.1.4" +name = "bumpalo" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" +checksum = "c764d619ca78fccbf3069b37bd7af92577f044bb15236036662d79b6559f25b7" [[package]] -name = "bumpalo" -version = "3.15.0" +name = "bytes" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" [[package]] name = "cfg-if" @@ -129,7 +177,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets", + "windows-targets 0.52.0", ] [[package]] @@ -165,16 +213,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -202,9 +240,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.6" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -212,58 +250,58 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.6" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim 0.10.0", - "syn", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.20.6" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "derive_builder" -version = "0.20.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" +checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.20.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" +checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "derive_builder_macro" -version = "0.20.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" +checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ "derive_builder_core", - "syn", + "syn 1.0.109", ] [[package]] @@ -276,66 +314,36 @@ dependencies = [ "crypto-common", ] -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys", -] - [[package]] name = "faktory" version = "0.12.5" dependencies = [ - "bufstream", + "async-trait", "chrono", "clap", "derive_builder", "fnv", "hostname", "libc", - "mockstream", - "native-tls", - "openssl", + "pin-project", "rand", "serde", "serde_derive", "serde_json", "sha2", "thiserror", + "tokio", + "tokio-rustls", + "tokio-test", "url", ] -[[package]] -name = "fastrand" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -345,6 +353,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + [[package]] name = "generic-array" version = "0.14.7" @@ -366,6 +380,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hermit-abi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" + [[package]] name = "hostname" version = "0.3.1" @@ -431,24 +457,12 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - [[package]] name = "log" version = "0.4.20" @@ -462,27 +476,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] -name = "mockstream" -version = "0.0.3" +name = "memchr" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bbe0c0c9d254b463b13734bc361d1423289547e052b1e77e5a77292496ba2e" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] [[package]] -name = "native-tls" -version = "0.2.11" +name = "mio" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ - "lazy_static", "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", + "wasi", + "windows-sys 0.48.0", ] [[package]] @@ -495,66 +511,61 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "openssl" -version = "0.10.63" +name = "num_cpus" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "bitflags 2.4.2", - "cfg-if", - "foreign-types", + "hermit-abi", "libc", - "once_cell", - "openssl-macros", - "openssl-sys", ] [[package]] -name = "openssl-macros" -version = "0.1.1" +name = "object" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ - "proc-macro2", - "quote", - "syn", + "memchr", ] [[package]] -name = "openssl-probe" -version = "0.1.5" +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "percent-encoding" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "openssl-sys" -version = "0.9.99" +name = "pin-project" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "pin-project-internal", ] [[package]] -name = "percent-encoding" -version = "2.3.1" +name = "pin-project-internal" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] [[package]] -name = "pkg-config" -version = "0.3.30" +name = "pin-project-lite" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "ppv-lite86" @@ -611,81 +622,88 @@ dependencies = [ ] [[package]] -name = "rustix" -version = "0.38.31" +name = "ring" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ - "bitflags 2.4.2", - "errno", + "cc", + "cfg-if", + "getrandom", "libc", - "linux-raw-sys", - "windows-sys", + "spin", + "untrusted", + "windows-sys 0.52.0", ] [[package]] -name = "ryu" -version = "1.0.16" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "schannel" -version = "0.1.23" +name = "rustls" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" dependencies = [ - "windows-sys", + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", ] [[package]] -name = "security-framework" -version = "2.9.2" +name = "rustls-pki-types" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] +checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" [[package]] -name = "security-framework-sys" -version = "2.9.1" +name = "rustls-webpki" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ - "core-foundation-sys", - "libc", + "ring", + "rustls-pki-types", + "untrusted", ] +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -703,6 +721,22 @@ dependencies = [ "digest", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "strsim" version = "0.10.0" @@ -715,11 +749,17 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" -version = "2.0.49" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -727,15 +767,14 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.10.0" +name = "syn" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -755,7 +794,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", ] [[package]] @@ -773,6 +812,69 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-test" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" +dependencies = [ + "async-stream", + "bytes", + "futures-core", + "tokio", + "tokio-stream", +] + [[package]] name = "typenum" version = "1.17.0" @@ -793,13 +895,19 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -817,12 +925,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" @@ -856,7 +958,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.50", "wasm-bindgen-shared", ] @@ -878,7 +980,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.50", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -917,7 +1019,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -926,7 +1037,22 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -935,53 +1061,101 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index 6e534db0..3929cee4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,33 +15,42 @@ exclude = [".github", "docker", ".gitignore", "Makefile"] [features] default = [] -tls = ["native-tls"] +tls = ["tokio-rustls", "pin-project"] binaries = ["clap"] ent = [] [dependencies] -bufstream = "0.1" -chrono = { version = "0.4", features = ["serde", "clock"], default-features = false } +async-trait = "0.1.77" clap = { version = "4.4.10", optional = true } -derive_builder = "0.20.0" +chrono = { version = "0.4", features = [ + "serde", + "clock", +], default-features = false } +derive_builder = "0.12.0" fnv = "1.0.5" hostname = "0.3" libc = "0.2" -native-tls = { version = "0.2", optional = true } +pin-project = { version = "1.1.4", optional = true } rand = "0.8" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" sha2 = "0.10.0" thiserror = "1.0.30" +tokio = { version = "1.35.1", features = [ + "io-util", + "net", + "rt", + "time", + "macros", + "rt-multi-thread", +] } +tokio-rustls = { version = "0.25.0", optional = true } url = "2" [dev-dependencies] -mockstream = "0.0.3" - -# to make -Zminimal-versions work -[target.'cfg(any())'.dependencies] -openssl = { version = "0.10.60", optional = true } +tokio = { version = "1.35.1", features = ["rt", "macros"] } +tokio-test = "0.4.3" [[bin]] name = "loadtest" diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index e5a24e41..1b21368a 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -5,15 +5,15 @@ use rand::prelude::*; use std::io; use std::process; use std::sync::{self, atomic}; -use std::thread; use std::time; const QUEUES: &[&str] = &["queue0", "queue1", "queue2", "queue3", "queue4"]; -fn main() { - let matches = Command::new("My Super Program") +#[tokio::main] +async fn main() { + let matches = Command::new("My Super Program (Async)") .version("0.1") - .about("Benchmark the performance of Rust Faktory consumers and producers") + .about("Benchmark the performance of Rust Faktory async consumers and producers") .arg( Arg::new("jobs") .help("Number of jobs to run") @@ -37,8 +37,10 @@ fn main() { jobs, threads ); - // ensure that we can actually connect to the server - if let Err(e) = Producer::connect(None) { + // ensure that we can actually connect to the server; + // will create a client, run a handshake with Faktory, + // and drop the cliet immediately afterwards; + if let Err(e) = Producer::connect(None).await { println!("{}", e); process::exit(1); } @@ -47,25 +49,27 @@ fn main() { let popped = sync::Arc::new(atomic::AtomicUsize::new(0)); let start = time::Instant::now(); - let threads: Vec>> = (0..threads) + let threads: Vec>> = (0..threads) .map(|_| { let pushed = sync::Arc::clone(&pushed); let popped = sync::Arc::clone(&popped); - thread::spawn(move || { + tokio::spawn(async move { // make producer and consumer - let mut p = Producer::connect(None).unwrap(); + let mut p = Producer::connect(None).await.unwrap(); let mut c = ConsumerBuilder::default(); c.register("SomeJob", |_| { - let mut rng = rand::thread_rng(); - if rng.gen_bool(0.01) { - Err(io::Error::new(io::ErrorKind::Other, "worker closed")) - } else { - Ok(()) - } + Box::pin(async move { + let mut rng = rand::thread_rng(); + if rng.gen_bool(0.01) { + Err(io::Error::new(io::ErrorKind::Other, "worker closed")) + } else { + Ok(()) + } + }) }); - let mut c = c.connect(None).unwrap(); + let mut c = c.connect(None).await.unwrap(); - let mut rng = rand::thread_rng(); + let mut rng = rand::rngs::OsRng; let mut random_queues = Vec::from(QUEUES); random_queues.shuffle(&mut rng); for idx in 0..jobs { @@ -77,13 +81,13 @@ fn main() { ); job.priority = Some(rng.gen_range(1..10)); job.queue = QUEUES.choose(&mut rng).unwrap().to_string(); - p.enqueue(job)?; + p.enqueue(job).await?; if pushed.fetch_add(1, atomic::Ordering::SeqCst) >= jobs { return Ok(idx); } } else { // pop - c.run_one(0, &random_queues[..])?; + c.run_one(0, &random_queues[..]).await?; if popped.fetch_add(1, atomic::Ordering::SeqCst) >= jobs { return Ok(idx); } @@ -94,7 +98,10 @@ fn main() { }) .collect(); - let _ops_count: Result, _> = threads.into_iter().map(|jt| jt.join().unwrap()).collect(); + let mut _ops_count = Vec::with_capacity(threads.len()); + for jh in threads { + _ops_count.push(jh.await.unwrap()) + } let stop = start.elapsed(); let stop_secs = stop.as_secs() * 1_000_000_000 + u64::from(stop.subsec_nanos()); let stop_secs = stop_secs as f64 / 1_000_000_000.0; diff --git a/src/consumer/builder.rs b/src/consumer/builder.rs new file mode 100644 index 00000000..4b558425 --- /dev/null +++ b/src/consumer/builder.rs @@ -0,0 +1,131 @@ +use super::{runner::Closure, CallbacksRegistry, Client, Consumer}; +use crate::{ + proto::{ + utils::{get_env_url, host_from_url, url_parse}, + ClientOptions, + }, + Error, Job, JobRunner, +}; +use std::future::Future; +use std::pin::Pin; +use tokio::io::{AsyncRead, AsyncWrite, BufStream}; +use tokio::net::TcpStream as TokioStream; + +/// Convenience wrapper for building a Faktory worker. +/// +/// See the [`Consumer`] documentation for details. +pub struct ConsumerBuilder { + opts: ClientOptions, + workers_count: usize, + callbacks: CallbacksRegistry, +} + +impl ConsumerBuilder { + /// Create a builder for asynchronous version of `Consumer`. + pub fn default_async() -> ConsumerBuilder { + ConsumerBuilder::default() + } +} + +impl Default for ConsumerBuilder { + /// Create a builder for asynchronous version of `Consumer`. + /// + /// See [`ConsumerBuilder`](struct.ConsumerBuilder.html) + fn default() -> Self { + ConsumerBuilder { + opts: ClientOptions::default(), + workers_count: 1, + callbacks: CallbacksRegistry::default(), + } + } +} + +impl ConsumerBuilder { + /// Set the hostname to use for this worker. + /// + /// Defaults to the machine's hostname as reported by the operating system. + pub fn hostname(&mut self, hn: String) -> &mut Self { + self.opts.hostname = Some(hn); + self + } + + /// Set a unique identifier for this worker. + /// + /// Defaults to a randomly generated 32-char ASCII string. + pub fn wid(&mut self, wid: String) -> &mut Self { + self.opts.wid = Some(wid); + self + } + + /// Set the labels to use for this worker. + /// + /// Defaults to `["rust"]`. + pub fn labels(&mut self, labels: Vec) -> &mut Self { + self.opts.labels = labels; + self + } + + /// Set the number of workers to use `run` and `run_to_completion`. + /// + /// Defaults to 1. + pub fn workers(&mut self, w: usize) -> &mut Self { + self.workers_count = w; + self + } + + /// Register a handler function for the given job type (`kind`). + /// + /// Whenever a job whose type matches `kind` is fetched from the Faktory, the given handler + /// function is called with that job as its argument. + pub fn register(&mut self, kind: K, handler: H) -> &mut Self + where + K: Into, + H: Send + Sync + Fn(Job) -> Pin> + Send>> + 'static, + { + self.register_runner(kind, Closure(handler)); + self + } + + /// Register a handler for the given job type (`kind`). + /// + /// Whenever a job whose type matches `kind` is fetched from the Faktory, the given handler + /// object is called with that job as its argument. + pub fn register_runner(&mut self, kind: K, runner: H) -> &mut Self + where + K: Into, + H: JobRunner + 'static, + { + self.callbacks.insert(kind.into(), Box::new(runner)); + self + } + + /// Asynchronously connect to a Faktory server with a non-standard stream. + pub async fn connect_with( + mut self, + stream: S, + pwd: Option, + ) -> Result, E>, Error> { + self.opts.password = pwd; + self.opts.is_worker = true; + let buffered = BufStream::new(stream); + let client = Client::new(buffered, self.opts).await?; + Ok(Consumer::new(client, self.workers_count, self.callbacks).await) + } + + /// Asynchronously connect to a Faktory server. + /// + /// See [`connect`](struct.ConsumerBuilder.html#structmethod.connect). + pub async fn connect( + self, + url: Option<&str>, + ) -> Result, E>, Error> { + let url = match url { + Some(url) => url_parse(url), + None => url_parse(&get_env_url()), + }?; + let stream = TokioStream::connect(host_from_url(&url)).await?; + let buffered = BufStream::new(stream); + let client = Client::new(buffered, self.opts).await?; + Ok(Consumer::new(client, self.workers_count, self.callbacks).await) + } +} diff --git a/src/consumer/health.rs b/src/consumer/health.rs new file mode 100644 index 00000000..50ff2def --- /dev/null +++ b/src/consumer/health.rs @@ -0,0 +1,78 @@ +use super::{Consumer, STATUS_QUIET, STATUS_RUNNING, STATUS_TERMINATING}; +use crate::{proto::HeartbeatStatus, Error, Reconnect}; +use std::{ + error::Error as StdError, + sync::{atomic, Arc}, + time, +}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::time::sleep as tokio_sleep; + +impl< + S: AsyncBufReadExt + AsyncWriteExt + Reconnect + Send + Unpin + 'static, + E: StdError + 'static + Send, + > Consumer +{ + pub(crate) async fn listen_for_heartbeats( + &mut self, + statuses: &Vec>, + ) -> Result { + let mut target = STATUS_RUNNING; + + let mut last = time::Instant::now(); + + loop { + tokio_sleep(time::Duration::from_millis(100)).await; + + // has a worker failed? + if target == STATUS_RUNNING + && statuses + .iter() + .any(|s| s.load(atomic::Ordering::SeqCst) == STATUS_TERMINATING) + { + // tell all workers to exit + // (though chances are they've all failed already) + for s in statuses { + s.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); + } + break Ok(false); + } + + if last.elapsed().as_secs() < 5 { + // don't sent a heartbeat yet + continue; + } + + match self.c.heartbeat().await { + Ok(hb) => { + match hb { + HeartbeatStatus::Ok => {} + HeartbeatStatus::Quiet => { + // tell the workers to eventually terminate + for s in statuses { + s.store(STATUS_QUIET, atomic::Ordering::SeqCst); + } + target = STATUS_QUIET; + } + HeartbeatStatus::Terminate => { + // tell the workers to terminate + // *and* fail the current job and immediately return + for s in statuses { + s.store(STATUS_QUIET, atomic::Ordering::SeqCst); + } + break Ok(true); + } + } + } + Err(e) => { + // for this to fail, the workers have probably also failed + for s in statuses { + s.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); + } + break Err(e); + } + } + last = time::Instant::now(); + } + } +} diff --git a/src/consumer/mod.rs b/src/consumer/mod.rs index 119b64f3..2139bc76 100644 --- a/src/consumer/mod.rs +++ b/src/consumer/mod.rs @@ -1,100 +1,25 @@ -use crate::error::Error; -use crate::proto::{ - self, parse_provided_or_from_env, Ack, Client, ClientOptions, Fail, HeartbeatStatus, Job, - Reconnect, +use super::proto::{Client, Reconnect}; +use crate::{ + proto::{Ack, Fail}, + Error, Job, }; -use fnv::FnvHashMap; -use std::error::Error as StdError; -use std::io::prelude::*; -use std::net::TcpStream; -use std::sync::{atomic, Arc, Mutex}; +use std::sync::{atomic, Arc}; +use std::{error::Error as StdError, sync::atomic::AtomicUsize}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::task::JoinHandle; -const STATUS_RUNNING: usize = 0; -const STATUS_QUIET: usize = 1; -const STATUS_TERMINATING: usize = 2; +mod builder; +mod health; +mod registries; +mod runner; -/// Implementations of this trait can be registered to run jobs in a `Consumer`. -/// -/// # Example -/// -/// Create a worker with all default options, register a single handler (for the `foo` job -/// type), connect to the Faktory server, and start accepting jobs. -/// The handler is a struct that implements `JobRunner`. -/// -/// ```no_run -/// use faktory::{ConsumerBuilder, JobRunner, Job}; -/// use std::io; -/// -/// struct MyHandler { -/// config: String, -/// } -/// impl JobRunner for MyHandler { -/// type Error = io::Error; -/// fn run(&self, job: Job) -> Result<(), Self::Error> { -/// println!("config: {}", self.config); -/// println!("job: {:?}", job); -/// Ok(()) -/// } -/// } -/// -/// let mut c = ConsumerBuilder::default(); -/// let handler = MyHandler { -/// config: "bar".to_string(), -/// }; -/// c.register_runner("foo", handler); -/// let mut c = c.connect(None).unwrap(); -/// if let Err(e) = c.run(&["default"]) { -/// println!("worker failed: {}", e); -/// } -/// ``` -pub trait JobRunner: Send + Sync { - /// The error type that the handler may return. - type Error; - /// A handler function that runs a job. - fn run(&self, job: Job) -> Result<(), Self::Error>; -} -type BoxedJobRunner = Box>; -// Implements JobRunner for a closure that takes a Job and returns a Result<(), E> -impl JobRunner for Box -where - F: Fn(Job) -> Result<(), E> + Send + Sync, -{ - type Error = E; - fn run(&self, job: Job) -> Result<(), E> { - self(job) - } -} +pub use builder::ConsumerBuilder; +use registries::{CallbacksRegistry, StatesRegistry}; +pub use runner::JobRunner; -// Additional Blanket Implementations -impl<'a, E, F> JobRunner for &'a F -where - F: Fn(Job) -> Result<(), E> + Send + Sync, -{ - type Error = E; - fn run(&self, job: Job) -> Result<(), E> { - self(job) - } -} -impl<'a, E, F> JobRunner for &'a mut F -where - F: Fn(Job) -> Result<(), E> + Send + Sync, -{ - type Error = E; - fn run(&self, job: Job) -> Result<(), E> { - (self as &F)(job) - } -} -#[repr(transparent)] -struct Closure(F); -impl JobRunner for Closure -where - F: Fn(Job) -> Result<(), E> + Send + Sync, -{ - type Error = E; - fn run(&self, job: Job) -> Result<(), E> { - (self.0)(job) - } -} +pub(crate) const STATUS_RUNNING: usize = 0; +pub(crate) const STATUS_QUIET: usize = 1; +pub(crate) const STATUS_TERMINATING: usize = 2; /// `Consumer` is used to run a worker that processes jobs provided by Faktory. /// @@ -165,9 +90,9 @@ where /// /// If all this process is doing is handling jobs, reconnecting on failure, and exiting when told /// to by the Faktory server, you should use -/// [`run_to_completion`](struct.Consumer.html#method.run_to_completion). If you want more +/// [`run_to_completion`](Consumer::run_to_completion). If you want more /// fine-grained control over the lifetime of your process, you should use -/// [`Consumer::run`](struct.Consumer.html#method.run). See the documentation for each of these +/// [`run`](Consumer::run). See the documentation for each of these /// methods for details. /// /// # Examples @@ -176,282 +101,201 @@ where /// type), connect to the Faktory server, and start accepting jobs. /// /// ```no_run +/// # tokio_test::block_on(async { /// use faktory::ConsumerBuilder; /// use std::io; +/// /// let mut c = ConsumerBuilder::default(); -/// c.register("foobar", |job| -> io::Result<()> { +/// c.register("foobar", |job| Box::pin(async move { /// println!("{:?}", job); -/// Ok(()) -/// }); -/// let mut c = c.connect(None).unwrap(); -/// if let Err(e) = c.run(&["default"]) { +/// Ok::<(), io::Error>(()) +/// })); +/// let mut c = c.connect(None).await.unwrap(); +/// if let Err(e) = c.run(&["default"]).await { /// println!("worker failed: {}", e); /// } +/// # }); /// ``` -pub struct Consumer -where - S: Read + Write, -{ +/// +pub struct Consumer { c: Client, - worker_states: Arc>>, - callbacks: Arc>>, + worker_states: Arc, + callbacks: Arc>, terminated: bool, } -#[derive(Default)] -struct WorkerState { - last_job_result: Option>, - running_job: Option, -} - -/// Convenience wrapper for building a Faktory worker. -/// -/// See the [`Consumer`](struct.Consumer.html) documentation for details. -pub struct ConsumerBuilder { - opts: ClientOptions, - workers: usize, - callbacks: FnvHashMap>, +impl Consumer { + async fn reconnect(&mut self) -> Result<(), Error> { + self.c.reconnect().await + } } -impl Default for ConsumerBuilder { - /// Construct a new worker with default worker options and the url fetched from environment - /// variables. - /// - /// This will construct a worker where: - /// - /// - `hostname` is this machine's hostname. - /// - `wid` is a randomly generated string. - /// - `pid` is the OS PID of this process. - /// - `labels` is `["rust"]`. - /// - fn default() -> Self { - ConsumerBuilder { - opts: ClientOptions::default(), - workers: 1, - callbacks: Default::default(), +impl Consumer { + async fn new(c: Client, workers_count: usize, callbacks: CallbacksRegistry) -> Self { + Consumer { + c, + callbacks: Arc::new(callbacks), + worker_states: Arc::new(StatesRegistry::new(workers_count)), + terminated: false, } } } -impl ConsumerBuilder { - /// Set the hostname to use for this worker. - /// - /// Defaults to the machine's hostname as reported by the operating system. - pub fn hostname(&mut self, hn: String) -> &mut Self { - self.opts.hostname = Some(hn); - self - } - - /// Set a unique identifier for this worker. - /// - /// Defaults to a randomly generated ASCII string. - pub fn wid(&mut self, wid: String) -> &mut Self { - self.opts.wid = Some(wid); - self - } - - /// Set the labels to use for this worker. - /// - /// Defaults to `["rust"]`. - pub fn labels(&mut self, labels: Vec) -> &mut Self { - self.opts.labels = labels; - self - } +enum Failed { + Application(E), + BadJobType(String), +} - /// Set the number of workers to use for `run` and `run_to_completion_*`. - /// - /// Defaults to 1. - pub fn workers(&mut self, w: usize) -> &mut Self { - self.workers = w; - self +impl + Consumer +{ + async fn run_job(&mut self, job: Job) -> Result<(), Failed> { + let handler = self + .callbacks + .get(job.kind()) + .ok_or(Failed::BadJobType(job.kind().to_string()))?; + handler.run(job).await.map_err(Failed::Application) } - /// Register a handler function for the given job type (`kind`). - /// - /// Whenever a job whose type matches `kind` is fetched from the Faktory, the given handler - /// function is called with that job as its argument. - pub fn register(&mut self, kind: K, handler: H) -> &mut Self - where - K: Into, - H: Fn(Job) -> Result<(), E> + Send + Sync + 'static, - { - self.register_runner(kind, Closure(handler)) + async fn report_failure_to_server(&mut self, f: &Fail) -> Result<(), Error> { + self.c.issue(f).await?.read_ok().await } - /// Register a handler for the given job type (`kind`). - /// - /// Whenever a job whose type matches `kind` is fetched from the Faktory, the given handler - /// object is called with that job as its argument. - pub fn register_runner(&mut self, kind: K, runner: H) -> &mut Self - where - K: Into, - H: JobRunner + 'static, - { - self.callbacks.insert(kind.into(), Box::new(runner)); - self + async fn report_success_to_server(&mut self, jid: impl Into) -> Result<(), Error> { + self.c.issue(&Ack::new(jid)).await?.read_ok().await } - /// Connect to a Faktory server. - /// - /// If `url` is not given, will use the standard Faktory environment variables. Specifically, - /// `FAKTORY_PROVIDER` is read to get the name of the environment variable to get the address - /// from (defaults to `FAKTORY_URL`), and then that environment variable is read to get the - /// server address. If the latter environment variable is not defined, the connection will be - /// made to - /// - /// ```text - /// tcp://localhost:7419 - /// ``` - /// - /// If `url` is given, but does not specify a port, it defaults to 7419. - pub fn connect(self, url: Option<&str>) -> Result, Error> { - let url = parse_provided_or_from_env(url)?; - let stream = TcpStream::connect(proto::host_from_url(&url))?; - Self::connect_with(self, stream, url.password().map(|p| p.to_string())) - } + async fn report_on_all_workers(&mut self) -> Result<(), Error> { + let worker_states = Arc::get_mut(&mut self.worker_states) + .expect("all workers are scoped to &mut of the user-code-visible Consumer"); - /// Connect to a Faktory server with a non-standard stream. - pub fn connect_with( - mut self, - stream: S, - pwd: Option, - ) -> Result, Error> { - self.opts.password = pwd; - self.opts.is_worker = true; - Ok(Consumer::new( - Client::new(stream, self.opts)?, - self.workers, - self.callbacks, - )) - } -} + // retry delivering notification about our last job result. + // we know there's no leftover thread at this point, so there's no race on the option. + for wstate in worker_states.iter_mut() { + let wstate = wstate.get_mut().unwrap(); + if let Some(res) = wstate.last_job_result.take() { + let r = match res { + Ok(ref jid) => self.c.issue(&Ack::new(jid)).await, + Err(ref fail) => self.c.issue(fail).await, + }; -enum Failed { - Application(E), - BadJobType(String), -} + let r = match r { + Ok(r) => r, + Err(e) => { + wstate.last_job_result = Some(res); + return Err(e); + } + }; -impl Consumer { - fn new(c: Client, workers: usize, callbacks: FnvHashMap>) -> Self { - Consumer { - c, - callbacks: Arc::new(callbacks), - worker_states: Arc::new((0..workers).map(|_| Default::default()).collect()), - terminated: false, + if let Err(e) = r.read_ok().await { + // it could be that the server did previously get our ACK/FAIL, and that it was + // the resulting OK that failed. in that case, we would get an error response + // when re-sending the job response. this should not count as critical. other + // errors, however, should! + if let Error::IO(_) = e { + wstate.last_job_result = Some(res); + return Err(e); + } + } + } } - } -} -impl Consumer { - fn reconnect(&mut self) -> Result<(), Error> { - self.c.reconnect() + Ok(()) } -} -impl Consumer -where - S: Read + Write, - E: StdError, -{ - fn run_job(&mut self, job: Job) -> Result<(), Failed> { - match self.callbacks.get(&job.kind) { - Some(callback) => callback.run(job).map_err(Failed::Application), - None => { - // cannot execute job, since no handler exists - Err(Failed::BadJobType(job.kind)) + // FAIL currently running jobs even though they're still running. + // Returns the number of workers that may still be processing jobs. + // We are ignoring any FAIL command issue errors, since this is already + // an "emergency" case. + async fn force_fail_all_workers(&mut self) -> usize { + let mut running = 0; + for wstate in self.worker_states.iter() { + let may_be_jid = wstate.lock().unwrap().running_job.take(); + if let Some(jid) = may_be_jid { + running += 1; + let f = Fail::new(&*jid, "unknown", "terminated"); + let _ = match self.c.issue(&f).await { + Ok(r) => r.read_ok().await, + Err(_) => continue, + } + .is_ok(); } } + running } - /// Fetch and run a single job on the current thread, and then return. - pub fn run_one(&mut self, worker: usize, queues: &[Q]) -> Result + /// Fetch and run a single job, and then return. + pub async fn run_one(&mut self, worker: usize, queues: &[Q]) -> Result where - Q: AsRef, + Q: AsRef + Sync, { - // get a job - let job = match self.c.fetch(queues)? { - Some(job) => job, + let job = match self.c.fetch(queues).await? { None => return Ok(false), + Some(j) => j, }; - // remember the job id let jid = job.jid.clone(); - // keep track of running job in case we're terminated during it - self.worker_states[worker].lock().unwrap().running_job = Some(jid.clone()); - - // process the job - let r = self.run_job(job); + self.worker_states.register_running(worker, jid.clone()); - // report back - match r { + match self.run_job(job).await { Ok(_) => { - // job done -- acknowledge - // remember it in case we fail to notify the server (e.g., broken connection) - self.worker_states[worker].lock().unwrap().last_job_result = Some(Ok(jid.clone())); - self.c.issue(&Ack::new(jid))?.await_ok()?; + self.worker_states.register_success(worker, jid.clone()); + self.report_success_to_server(jid).await?; } Err(e) => { - // job failed -- let server know - // "unknown" is the errtype used by the go library too let fail = match e { - Failed::BadJobType(jt) => { - Fail::new(jid, "unknown", format!("No handler for {}", jt)) - } - Failed::Application(e) => { - let mut f = Fail::new(jid, "unknown", format!("{}", e)); - let mut root = e.source(); - let mut backtrace = Vec::new(); - while let Some(r) = root.take() { - backtrace.push(format!("{}", r)); - root = r.source(); - } - f.set_backtrace(backtrace); - f - } + Failed::BadJobType(jt) => Fail::generic(jid, format!("No handler for {}", jt)), + Failed::Application(e) => Fail::generic_with_backtrace(jid, e), }; - - let fail2 = fail.clone(); - self.worker_states[worker].lock().unwrap().last_job_result = Some(Err(fail)); - self.c.issue(&fail2)?.await_ok()?; + self.worker_states.register_failure(worker, &fail); + self.report_failure_to_server(&fail).await?; } } - // we won't have to tell the server again - { - let mut state = self.worker_states[worker].lock().unwrap(); - state.last_job_result = None; - state.running_job = None; - } - Ok(true) - } + self.worker_states.reset(worker); - #[cfg(test)] - pub(crate) fn run_n(&mut self, n: usize, queues: &[Q]) -> Result<(), Error> - where - Q: AsRef, - { - for _ in 0..n { - self.run_one(0, queues)?; - } - Ok(()) + Ok(true) } } -impl Consumer -where - S: Read + Write + Reconnect + Send + 'static, - E: StdError + 'static, +impl< + S: AsyncBufReadExt + AsyncWriteExt + Reconnect + Send + Unpin + 'static, + E: StdError + 'static + Send, + > Consumer { - fn for_worker(&mut self) -> Result { + async fn for_worker(&mut self) -> Result { Ok(Consumer { - c: self.c.connect_again()?, + c: self.c.connect_again().await?, callbacks: Arc::clone(&self.callbacks), worker_states: Arc::clone(&self.worker_states), terminated: self.terminated, }) } + async fn spawn_worker( + &mut self, + status: Arc, + worker: usize, + queues: &[Q], + ) -> Result>, Error> + where + Q: AsRef, + { + let mut w = self.for_worker().await?; + let queues: Vec<_> = queues.iter().map(|s| s.as_ref().to_string()).collect(); + Ok(tokio::spawn(async move { + while status.load(atomic::Ordering::SeqCst) == STATUS_RUNNING { + if let Err(e) = w.run_one(worker, &queues[..]).await { + status.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); + return Err(e); + } + } + status.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); + Ok(()) + })) + } + /// Run this worker on the given `queues` until an I/O error occurs (`Err` is returned), or /// until the server tells the worker to disengage (`Ok` is returned). /// @@ -462,132 +306,29 @@ where /// a job success or failure, the result will be re-reported to the server without re-executing /// the job. If the worker was terminated (i.e., `run` returns with an `Ok` response), the /// worker should **not** try to resume by calling `run` again. This will cause a panic. - pub fn run(&mut self, queues: &[Q]) -> Result + pub async fn run(&mut self, queues: &[Q]) -> Result where Q: AsRef, { assert!(!self.terminated, "do not re-run a terminated worker"); - let worker_states = Arc::get_mut(&mut self.worker_states) - .expect("all workers are scoped to &mut of the user-code-visible Consumer"); - - // retry delivering notification about our last job result. - // we know there's no leftover thread at this point, so there's no race on the option. - for wstate in worker_states.iter_mut() { - let wstate = wstate.get_mut().unwrap(); - if let Some(res) = wstate.last_job_result.take() { - let r = match res { - Ok(ref jid) => self.c.issue(&Ack::new(&**jid)), - Err(ref fail) => self.c.issue(fail), - }; - - let r = match r { - Ok(r) => r, - Err(e) => { - wstate.last_job_result = Some(res); - return Err(e); - } - }; + self.report_on_all_workers().await?; - if let Err(e) = r.await_ok() { - // it could be that the server did previously get our ACK/FAIL, and that it was - // the resulting OK that failed. in that case, we would get an error response - // when re-sending the job response. this should not count as critical. other - // errors, however, should! - if let Error::IO(_) = e { - wstate.last_job_result = Some(res); - return Err(e); - } - } - } - } + let workers_count = self.worker_states.len(); // keep track of the current status of each worker - let status: Vec<_> = (0..self.worker_states.len()) + let statuses: Vec<_> = (0..workers_count) .map(|_| Arc::new(atomic::AtomicUsize::new(STATUS_RUNNING))) .collect(); - // start worker threads - use std::thread; - let workers = status - .iter() - .enumerate() - .map(|(worker, status)| { - let mut w = self.for_worker()?; - let status = Arc::clone(status); - let queues: Vec<_> = queues.iter().map(|s| s.as_ref().to_string()).collect(); - Ok(thread::spawn(move || { - while status.load(atomic::Ordering::SeqCst) == STATUS_RUNNING { - if let Err(e) = w.run_one(worker, &queues[..]) { - status.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); - return Err(e); - } - } - status.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); - Ok(()) - })) - }) - .collect::, Error>>()?; - - // listen for heartbeats - let mut target = STATUS_RUNNING; - let exit = { - use std::time; - let mut last = time::Instant::now(); - - loop { - thread::sleep(time::Duration::from_millis(100)); - - // has a worker failed? - if target == STATUS_RUNNING - && status - .iter() - .any(|s| s.load(atomic::Ordering::SeqCst) == STATUS_TERMINATING) - { - // tell all workers to exit - // (though chances are they've all failed already) - for s in &status { - s.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); - } - break Ok(false); - } + let mut workers = Vec::with_capacity(workers_count); + for (worker, status) in statuses.iter().enumerate().take(workers_count) { + let handle = self + .spawn_worker(Arc::clone(status), worker, queues) + .await?; + workers.push(handle) + } - if last.elapsed().as_secs() < 5 { - // don't sent a heartbeat yet - continue; - } - - match self.c.heartbeat() { - Ok(hb) => { - match hb { - HeartbeatStatus::Ok => {} - HeartbeatStatus::Quiet => { - // tell the workers to eventually terminate - for s in &status { - s.store(STATUS_QUIET, atomic::Ordering::SeqCst); - } - target = STATUS_QUIET; - } - HeartbeatStatus::Terminate => { - // tell the workers to terminate - // *and* fail the current job and immediately return - for s in &status { - s.store(STATUS_QUIET, atomic::Ordering::SeqCst); - } - break Ok(true); - } - } - } - Err(e) => { - // for this to fail, the workers have probably also failed - for s in &status { - s.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); - } - break Err(e); - } - } - last = time::Instant::now(); - } - }; + let exit = self.listen_for_heartbeats(&statuses).await; // there are a couple of cases here: // @@ -596,56 +337,37 @@ where // - we got an error from heartbeat() // self.terminated = exit.is_ok(); - if let Ok(true) = exit { - // FAIL currently running jobs even though they're still running - let mut running = 0; - for wstate in self.worker_states.iter() { - if let Some(jid) = wstate.lock().unwrap().running_job.take() { - let f = Fail::new(&*jid, "unknown", "terminated"); - - // if this fails, we don't want to exit with Err(), - // because we *were* still terminated! - let _ = self.c.issue(&f).and_then(|r| r.await_ok()).is_ok(); - - running += 1; - } - } + if let Ok(true) = exit { + let running = self.force_fail_all_workers().await; if running != 0 { return Ok(running); } } + // we want to expose worker errors, or otherwise the heartbeat error + let mut results = Vec::with_capacity(workers_count); + for w in workers { + results.push(w.await.expect("joined ok")); + } + let result = results.into_iter().collect::, _>>(); + match exit { - Ok(_) => { - // we want to expose any worker errors - workers - .into_iter() - .map(|w| w.join().unwrap()) - .collect::, _>>() - .map(|_| 0) - } - Err(e) => { - // we want to expose worker errors, or otherwise the heartbeat error - workers - .into_iter() - .map(|w| w.join().unwrap()) - .collect::, _>>() - .and(Err(e)) - } + Ok(_) => result.map(|_| 0), + Err(e) => result.and(Err(e)), } } /// Run this worker until the server tells us to exit or a connection cannot be re-established. /// /// This function never returns. When the worker decides to exit, the process is terminated. - pub fn run_to_completion(mut self, queues: &[Q]) -> ! + pub async fn run_to_completion(mut self, queues: &[Q]) -> ! where Q: AsRef, { use std::process; - while self.run(queues).is_err() { - if self.reconnect().is_err() { + while self.run(queues).await.is_err() { + if self.reconnect().await.is_err() { break; } } @@ -653,34 +375,3 @@ where process::exit(0); } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - // https://github.com/rust-lang/rust/pull/42219 - //#[allow_fail] - #[ignore] - fn it_works() { - use crate::producer::Producer; - use std::io; - - let mut p = Producer::connect(None).unwrap(); - let mut j = Job::new("foobar", vec!["z"]); - j.queue = "worker_test_1".to_string(); - p.enqueue(j).unwrap(); - - let mut c = ConsumerBuilder::default(); - c.register("foobar", |job: Job| -> Result<(), io::Error> { - assert_eq!(job.args, vec!["z"]); - Ok(()) - }); - let mut c = c.connect(None).unwrap(); - let e = c.run_n(1, &["worker_test_1"]); - if e.is_err() { - println!("{:?}", e); - } - assert!(e.is_ok()); - } -} diff --git a/src/consumer/registries.rs b/src/consumer/registries.rs new file mode 100644 index 00000000..9cec6c4f --- /dev/null +++ b/src/consumer/registries.rs @@ -0,0 +1,77 @@ +use super::runner::BoxedJobRunner; +use crate::proto::Fail; +use fnv::FnvHashMap; +use std::{ + ops::{Deref, DerefMut}, + sync::Mutex, +}; + +// --------------- CALLBACKS (Job Handlers) ---------------- + +pub(crate) struct CallbacksRegistry(FnvHashMap>); + +impl Deref for CallbacksRegistry { + type Target = FnvHashMap>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for CallbacksRegistry { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Default for CallbacksRegistry { + fn default() -> CallbacksRegistry { + Self(FnvHashMap::default()) + } +} + +// -------------------- WORKER STATES --------------------- + +#[derive(Default)] +pub(crate) struct WorkerState { + pub(crate) last_job_result: Option>, + pub(crate) running_job: Option, +} + +pub(crate) struct StatesRegistry(Vec>); + +impl Deref for StatesRegistry { + type Target = Vec>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for StatesRegistry { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl StatesRegistry { + pub(crate) fn new(workers_count: usize) -> Self { + Self((0..workers_count).map(|_| Default::default()).collect()) + } + + pub(crate) fn register_running(&self, worker: usize, jid: String) { + self[worker].lock().expect("lock acquired").running_job = Some(jid); + } + + pub(crate) fn register_success(&self, worker: usize, jid: String) { + self[worker].lock().expect("lock acquired").last_job_result = Some(Ok(jid)); + } + + pub(crate) fn register_failure(&self, worker: usize, f: &Fail) { + self[worker].lock().expect("lock acquired").last_job_result = Some(Err(f.clone())); + } + + pub(crate) fn reset(&self, worker: usize) { + let mut state = self[worker].lock().expect("lock acquired"); + state.last_job_result = None; + state.running_job = None; + } +} diff --git a/src/consumer/runner.rs b/src/consumer/runner.rs new file mode 100644 index 00000000..6a181bc2 --- /dev/null +++ b/src/consumer/runner.rs @@ -0,0 +1,99 @@ +use crate::Job; +use std::{future::Future, pin::Pin}; + +/// Implementations of this trait can be registered to run jobs in a `Consumer`. +/// +/// # Example +/// +/// Create a worker with all default options, register a single handler (for the `foo` job +/// type), connect to the Faktory server, and start accepting jobs. +/// The handler is a struct that implements `JobRunner`. +/// +/// ```no_run +/// # tokio_test::block_on(async { +/// use faktory::{ConsumerBuilder, JobRunner, Job}; +/// use std::io; +/// +/// struct MyHandler { +/// config: String, +/// } +/// +/// #[async_trait::async_trait] +/// impl JobRunner for MyHandler { +/// type Error = io::Error; +/// async fn run(&self, job: Job) -> Result<(), Self::Error> { +/// println!("config: {}", self.config); +/// println!("job: {:?}", job); +/// Ok(()) +/// } +/// } +/// +/// let mut c = ConsumerBuilder::default(); +/// let handler = MyHandler { +/// config: "bar".to_string(), +/// }; +/// c.register_runner("foo", handler); +/// let mut c = c.connect(None).await.unwrap(); +/// if let Err(e) = c.run(&["default"]).await { +/// println!("worker failed: {}", e); +/// } +/// }); +/// ``` +#[async_trait::async_trait] +pub trait JobRunner: Send + Sync { + /// The error type that the handler may return. + type Error; + /// A handler function that runs a job. + async fn run(&self, job: Job) -> Result<(), Self::Error>; +} + +// Implements JobRunner for a closure that takes a Job and returns a Result<(), E> +#[async_trait::async_trait] +impl JobRunner for Box +where + F: Send + Sync + Fn(Job) -> Pin> + Send>>, +{ + type Error = E; + async fn run(&self, job: Job) -> Result<(), E> { + self(job).await + } +} + +// Additional Blanket Implementations +#[async_trait::async_trait] +impl<'a, E, F> JobRunner for &'a F +where + F: Send + Sync + Fn(Job) -> Pin> + Send>>, +{ + type Error = E; + async fn run(&self, job: Job) -> Result<(), E> { + self(job).await + } +} + +#[async_trait::async_trait] +impl<'a, E, F> JobRunner for &'a mut F +where + F: Send + Sync + Fn(Job) -> Pin> + Send>>, +{ + type Error = E; + async fn run(&self, job: Job) -> Result<(), E> { + (self as &F)(job).await + } +} + +#[repr(transparent)] +pub(crate) struct Closure(pub F); + +#[async_trait::async_trait] +impl JobRunner for Closure +where + F: Send + Sync + Fn(Job) -> Pin> + Send>>, +{ + type Error = E; + async fn run(&self, job: Job) -> Result<(), E> { + (self.0)(job).await + } +} + +pub(crate) type BoxedJobRunner = Box>; diff --git a/src/error.rs b/src/error.rs index 89de50ff..417ec6eb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,12 +41,6 @@ pub enum Error { /// These generally indicate a mismatch between what the client expects and what the server provided. #[error("serialization")] Serialization(#[source] serde_json::Error), - - /// Indicates an error in the underlying TLS stream. - #[cfg(feature = "tls")] - #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] - #[error("underlying tls stream")] - TlsStream(#[source] native_tls::Error), } /// Errors specific to connection logic. diff --git a/src/lib.rs b/src/lib.rs index a2ffaa31..0da91716 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,29 +34,32 @@ //! If you want to **submit** jobs to Faktory, use `Producer`. //! //! ```no_run +//! # tokio_test::block_on(async { //! use faktory::{Producer, Job}; -//! let mut p = Producer::connect(None).unwrap(); -//! p.enqueue(Job::new("foobar", vec!["z"])).unwrap(); +//! let mut p = Producer::connect(None).await.unwrap(); +//! p.enqueue(Job::new("foobar", vec!["z"])).await.unwrap(); //! -//! let (enqueued_count, errors) = p.enqueue_many(vec![Job::new("foobar", vec!["z"]), Job::new("foobar", vec!["z"])]).unwrap(); +//! let (enqueued_count, errors) = p.enqueue_many([Job::new("foobar", vec!["z"]), Job::new("foobar", vec!["z"])]).await.unwrap(); //! assert_eq!(enqueued_count, 2); //! assert_eq!(errors, None); +//! }); //! ``` -//! //! If you want to **accept** jobs from Faktory, use `Consumer`. //! //! ```no_run +//! # tokio_test::block_on(async { //! use faktory::ConsumerBuilder; //! use std::io; //! let mut c = ConsumerBuilder::default(); -//! c.register("foobar", |job| -> io::Result<()> { +//! c.register("foobar", |job| Box::pin(async move { //! println!("{:?}", job); -//! Ok(()) -//! }); -//! let mut c = c.connect(None).unwrap(); -//! if let Err(e) = c.run(&["default"]) { +//! Ok::<(), io::Error>(()) +//! })); +//! let mut c = c.connect(None).await.unwrap(); +//! if let Err(e) = c.run(&["default"]).await { //! println!("worker failed: {}", e); //! } +//! # }); //! ``` #![deny(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg))] diff --git a/src/producer/mod.rs b/src/producer/mod.rs index c92b111c..8589d816 100644 --- a/src/producer/mod.rs +++ b/src/producer/mod.rs @@ -1,8 +1,9 @@ -use crate::error::Error; -use crate::proto::{Client, Info, Job, Push, PushBulk, QueueAction, QueueControl}; use std::collections::HashMap; -use std::io::prelude::*; -use std::net::TcpStream; + +use crate::proto::{Client, Info, Job, Push, PushBulk, QueueAction, QueueControl}; +use crate::Error; +use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufStream}; +use tokio::net::TcpStream as TokioStream; #[cfg(feature = "ent")] use crate::proto::{Batch, BatchHandle, CommitBatch, OpenBatch}; @@ -46,63 +47,42 @@ use crate::proto::{Batch, BatchHandle, CommitBatch, OpenBatch}; /// Connecting to an unsecured Faktory server using environment variables /// /// ```no_run +/// # tokio_test::block_on(async { /// use faktory::Producer; -/// let p = Producer::connect(None).unwrap(); +/// let p = Producer::connect(None).await.unwrap(); +/// # }); /// ``` /// /// Connecting to a secured Faktory server using an explicit URL /// /// ```no_run +/// # tokio_test::block_on(async { /// use faktory::Producer; -/// let p = Producer::connect(Some("tcp://:hunter2@localhost:7439")).unwrap(); +/// let p = Producer::connect(Some("tcp://:hunter2@localhost:7439")).await.unwrap(); +/// # }) /// ``` /// /// Issuing a job using a `Producer` /// /// ```no_run +/// # tokio_test::block_on(async { /// # use faktory::Producer; -/// # let mut p = Producer::connect(None).unwrap(); +/// # let mut p = Producer::connect(None).await.unwrap(); /// use faktory::Job; -/// p.enqueue(Job::new("foobar", vec!["z"])).unwrap(); +/// p.enqueue(Job::new("foobar", vec!["z"])).await.unwrap(); +/// # }); /// ``` /// -// TODO: provide way of inspecting status of job. -pub struct Producer { +pub struct Producer { c: Client, } -impl Producer { - /// Connect to a Faktory server. - /// - /// If `url` is not given, will use the standard Faktory environment variables. Specifically, - /// `FAKTORY_PROVIDER` is read to get the name of the environment variable to get the address - /// from (defaults to `FAKTORY_URL`), and then that environment variable is read to get the - /// server address. If the latter environment variable is not defined, the connection will be - /// made to - /// - /// ```text - /// tcp://localhost:7419 - /// ``` - /// - /// If `url` is given, but does not specify a port, it defaults to 7419. - pub fn connect(url: Option<&str>) -> Result { - let c = Client::connect(url)?; - Ok(Producer { c }) - } -} - -impl Producer { - /// Connect to a Faktory server with a non-standard stream. - pub fn connect_with(stream: S, pwd: Option) -> Result, Error> { - let c = Client::connect_with(stream, pwd)?; - Ok(Producer { c }) - } - - /// Enqueue the given job on the Faktory server. +impl Producer { + /// Asynchronously enqueue the given job on the Faktory server. /// /// Returns `Ok` if the job was successfully queued by the Faktory server. - pub fn enqueue(&mut self, job: Job) -> Result<(), Error> { - self.c.issue(&Push::from(job))?.await_ok() + pub async fn enqueue(&mut self, job: Job) -> Result<(), Error> { + self.c.issue(&Push::from(job)).await?.read_ok().await } /// Enqueue numerous jobs on the Faktory server. @@ -116,7 +96,7 @@ impl Producer { /// /// Note that this is not an all-or-nothing operation: jobs that contain errors will not be enqueued, /// while those that are error-free _will_ be enqueued by the Faktory server. - pub fn enqueue_many( + pub async fn enqueue_many( &mut self, jobs: J, ) -> Result<(usize, Option>), Error> @@ -128,8 +108,10 @@ impl Producer { let jobs_count = jobs.len(); let errors: HashMap = self .c - .issue(&PushBulk::from(jobs.collect::>()))? - .read_json()? + .issue(&PushBulk::from(jobs.collect::>())) + .await? + .read_json() + .await? .expect("Faktory server sends {} literal when there are no errors"); if errors.is_empty() { return Ok((jobs_count, None)); @@ -140,32 +122,44 @@ impl Producer { /// Retrieve information about the running server. /// /// The returned value is the result of running the `INFO` command on the server. - pub fn info(&mut self) -> Result { + pub async fn info(&mut self) -> Result { self.c - .issue(&Info)? + .issue(&Info) + .await? .read_json() + .await .map(|v| v.expect("info command cannot give empty response")) } /// Pause the given queues. - pub fn queue_pause>(&mut self, queues: &[T]) -> Result<(), Error> { + pub async fn queue_pause(&mut self, queues: &[Q]) -> Result<(), Error> + where + Q: AsRef + Sync, + { self.c - .issue(&QueueControl::new(QueueAction::Pause, queues))? - .await_ok() + .issue(&QueueControl::new(QueueAction::Pause, queues)) + .await? + .read_ok() + .await } /// Resume the given queues. - pub fn queue_resume>(&mut self, queues: &[T]) -> Result<(), Error> { + pub async fn queue_resume>(&mut self, queues: &[Q]) -> Result<(), Error> + where + Q: AsRef + Sync, + { self.c - .issue(&QueueControl::new(QueueAction::Resume, queues))? - .await_ok() + .issue(&QueueControl::new(QueueAction::Resume, queues)) + .await? + .read_ok() + .await } /// Initiate a new batch of jobs. #[cfg(feature = "ent")] #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] - pub fn start_batch(&mut self, batch: Batch) -> Result, Error> { - let bid = self.c.issue(&batch)?.read_bid()?; + pub async fn start_batch(&mut self, batch: Batch) -> Result, Error> { + let bid = self.c.issue(&batch).await?.read_bid().await?; Ok(BatchHandle::new(bid, self)) } @@ -175,27 +169,50 @@ impl Producer { /// rather `Ok(None)` will be returned. #[cfg(feature = "ent")] #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] - pub fn open_batch(&mut self, bid: String) -> Result>, Error> { - let bid = self.c.issue(&OpenBatch::from(bid))?.maybe_bid()?; + pub async fn open_batch(&mut self, bid: String) -> Result>, Error> { + let bid = self + .c + .issue(&OpenBatch::from(bid)) + .await? + .maybe_bid() + .await?; Ok(bid.map(|bid| BatchHandle::new(bid, self))) } #[cfg(feature = "ent")] - pub(crate) fn commit_batch(&mut self, bid: String) -> Result<(), Error> { - self.c.issue(&CommitBatch::from(bid))?.await_ok() + pub(crate) async fn commit_batch(&mut self, bid: String) -> Result<(), Error> { + self.c.issue(&CommitBatch::from(bid)).await?.read_ok().await } } -#[cfg(test)] -mod tests { - use super::*; +impl Producer> { + /// Connect to a Faktory server with a non-standard stream. + pub async fn connect_with( + stream: S, + pwd: Option, + ) -> Result>, Error> { + let buffered = BufStream::new(stream); + let c = Client::connect_with(buffered, pwd).await?; + Ok(Producer { c }) + } +} - #[test] - // https://github.com/rust-lang/rust/pull/42219 - //#[allow_fail] - #[ignore] - fn it_works() { - let mut p = Producer::connect(None).unwrap(); - p.enqueue(Job::new("foobar", vec!["z"])).unwrap(); +impl Producer> { + /// Create a producer and asynchronously connect to a Faktory server. + /// + /// If `url` is not given, will use the standard Faktory environment variables. Specifically, + /// `FAKTORY_PROVIDER` is read to get the name of the environment variable to get the address + /// from (defaults to `FAKTORY_URL`), and then that environment variable is read to get the + /// server address. If the latter environment variable is not defined, the connection will be + /// made to + /// + /// ```text + /// tcp://localhost:7419 + /// ``` + /// + /// If `url` is given, but does not specify a port, it defaults to 7419. + pub async fn connect(url: Option<&str>) -> Result { + let c = Client::connect(url).await?; + Ok(Producer { c }) } } diff --git a/src/proto/batch/cmd.rs b/src/proto/batch/cmd.rs index a6c57367..106e0b8f 100644 --- a/src/proto/batch/cmd.rs +++ b/src/proto/batch/cmd.rs @@ -1,66 +1,43 @@ use crate::ent::Batch; use crate::proto::single::FaktoryCommand; use crate::Error; -use std::io::Write; +use tokio::io::AsyncWriteExt; +#[async_trait::async_trait] impl FaktoryCommand for Batch { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"BATCH NEW ")?; - serde_json::to_writer(&mut *w, self).map_err(Error::Serialization)?; - Ok(w.write_all(b"\r\n")?) + async fn issue(&self, w: &mut W) -> Result<(), Error> { + w.write_all(b"BATCH NEW ").await?; + let r = serde_json::to_vec(self).map_err(Error::Serialization)?; + w.write_all(&r).await?; + Ok(w.write_all(b"\r\n").await?) } } -// ---------------------------------------------- - -pub struct CommitBatch(String); - -impl From for CommitBatch { - fn from(value: String) -> Self { - CommitBatch(value) - } +macro_rules! batch_cmd { + ($structure:ident, $cmd:expr) => { + impl From for $structure { + fn from(value: String) -> Self { + $structure(value) + } + } + + #[async_trait::async_trait] + impl FaktoryCommand for $structure { + async fn issue(&self, w: &mut W) -> Result<(), Error> { + let c = format!("BATCH {} ", $cmd); + w.write_all(c.as_bytes()).await?; + w.write_all(self.0.as_bytes()).await?; + Ok(w.write_all(b"\r\n").await?) + } + } + }; } -impl FaktoryCommand for CommitBatch { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"BATCH COMMIT ")?; - w.write_all(self.0.as_bytes())?; - Ok(w.write_all(b"\r\n")?) - } -} - -// ---------------------------------------------- +pub struct CommitBatch(String); +batch_cmd!(CommitBatch, "COMMIT"); pub struct GetBatchStatus(String); - -impl From for GetBatchStatus { - fn from(value: String) -> Self { - GetBatchStatus(value) - } -} - -impl FaktoryCommand for GetBatchStatus { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"BATCH STATUS ")?; - w.write_all(self.0.as_bytes())?; - Ok(w.write_all(b"\r\n")?) - } -} - -// ---------------------------------------------- +batch_cmd!(GetBatchStatus, "STATUS"); pub struct OpenBatch(String); - -impl From for OpenBatch { - fn from(value: String) -> Self { - OpenBatch(value) - } -} - -impl FaktoryCommand for OpenBatch { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"BATCH OPEN ")?; - w.write_all(self.0.as_bytes())?; - Ok(w.write_all(b"\r\n")?) - } -} +batch_cmd!(OpenBatch, "OPEN"); diff --git a/src/proto/batch/mod.rs b/src/proto/batch/mod.rs index cd1134e1..7237c2b6 100644 --- a/src/proto/batch/mod.rs +++ b/src/proto/batch/mod.rs @@ -4,7 +4,7 @@ use crate::Client; use crate::{Error, Job, Producer}; use chrono::{DateTime, Utc}; use derive_builder::Builder; -use std::io::{Read, Write}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; mod cmd; @@ -27,10 +27,11 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// /// Here is how you can create a simple batch: /// ```no_run +/// # tokio_test::block_on(async { /// # use faktory::Error; /// use faktory::{Producer, Job, ent::Batch}; /// -/// let mut prod = Producer::connect(None)?; +/// let mut prod = Producer::connect(None).await?; /// let job1 = Job::builder("job_type").build(); /// let job2 = Job::builder("job_type").build(); /// let job_cb = Job::builder("callback_job_type").build(); @@ -39,19 +40,21 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// .description("Batch description") /// .with_complete_callback(job_cb); /// -/// let mut batch = prod.start_batch(batch)?; -/// batch.add(job1)?; -/// batch.add(job2)?; -/// batch.commit()?; +/// let mut batch = prod.start_batch(batch).await?; +/// batch.add(job1).await?; +/// batch.add(job2).await?; +/// batch.commit().await?; /// /// # Ok::<(), Error>(()) +/// # }); /// ``` /// /// Nested batches are also supported: /// ```no_run +/// # tokio_test::block_on(async { /// # use faktory::{Producer, Job, Error}; /// # use faktory::ent::Batch; -/// # let mut prod = Producer::connect(None)?; +/// # let mut prod = Producer::connect(None).await?; /// let parent_job1 = Job::builder("job_type").build(); /// let parent_job2 = Job::builder("another_job_type").build(); /// let parent_cb = Job::builder("callback_job_type").build(); @@ -67,17 +70,18 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// .description("Child batch description") /// .with_success_callback(child_cb); /// -/// let mut parent = prod.start_batch(parent_batch)?; -/// parent.add(parent_job1)?; -/// parent.add(parent_job2)?; -/// let mut child = parent.start_batch(child_batch)?; -/// child.add(child_job1)?; -/// child.add(child_job2)?; +/// let mut parent = prod.start_batch(parent_batch).await?; +/// parent.add(parent_job1).await?; +/// parent.add(parent_job2).await?; +/// let mut child = parent.start_batch(child_batch).await?; +/// child.add(child_job1).await?; +/// child.add(child_job2).await?; /// -/// child.commit()?; -/// parent.commit()?; +/// child.commit().await?; +/// parent.commit().await?; /// /// # Ok::<(), Error>(()) +/// }); /// ``` /// /// In the example above, there is a single level nesting, but you can nest those batches as deep as you wish, @@ -89,20 +93,21 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// # use faktory::Error; /// # use faktory::{Producer, Job, Client}; /// # use faktory::ent::{Batch, CallbackState}; -/// let mut prod = Producer::connect(None)?; +/// # tokio_test::block_on(async { +/// let mut prod = Producer::connect(None).await?; /// let job = Job::builder("job_type").build(); /// let cb_job = Job::builder("callback_job_type").build(); /// let b = Batch::builder() /// .description("Batch description") /// .with_complete_callback(cb_job); /// -/// let mut b = prod.start_batch(b)?; +/// let mut b = prod.start_batch(b).await?; /// let bid = b.id().to_string(); -/// b.add(job)?; -/// b.commit()?; +/// b.add(job).await?; +/// b.commit().await?; /// -/// let mut t = Client::connect(None)?; -/// let s = t.get_batch_status(bid)?.unwrap(); +/// let mut t = Client::connect(None).await?; +/// let s = t.get_batch_status(bid).await?.unwrap(); /// assert_eq!(s.total, 1); /// assert_eq!(s.pending, 1); /// assert_eq!(s.description, Some("Batch description".into())); @@ -112,6 +117,7 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// _ => panic!("The jobs of this batch have not executed, so the callback job is expected to _not_ have fired"), /// } /// # Ok::<(), Error>(()) +/// }); /// ``` #[derive(Builder, Debug, Serialize)] #[builder( @@ -206,12 +212,12 @@ impl Clone for BatchBuilder { } /// Represents a newly started or re-opened batch of jobs. -pub struct BatchHandle<'a, S: Read + Write> { +pub struct BatchHandle<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> { bid: String, prod: &'a mut Producer, } -impl<'a, S: Read + Write> BatchHandle<'a, S> { +impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { /// ID issued by the Faktory server to this batch. pub fn id(&self) -> &str { self.bid.as_ref() @@ -226,15 +232,15 @@ impl<'a, S: Read + Write> BatchHandle<'a, S> { /// Should the submitted job - for whatever reason - already have a `bid` key present in its custom hash, /// this value will be overwritten by the ID of the batch this job is being added to with the old value /// returned as `Some()`. - pub fn add(&mut self, mut job: Job) -> Result, Error> { + pub async fn add(&mut self, mut job: Job) -> Result, Error> { let bid = job.custom.insert("bid".into(), self.bid.clone().into()); - self.prod.enqueue(job).map(|_| bid) + self.prod.enqueue(job).await.map(|_| bid) } /// Initiate a child batch of jobs. - pub fn start_batch(&mut self, mut batch: Batch) -> Result, Error> { + pub async fn start_batch(&mut self, mut batch: Batch) -> Result, Error> { batch.parent_bid = Some(self.bid.clone()); - self.prod.start_batch(batch) + self.prod.start_batch(batch).await } /// Commit this batch. @@ -243,8 +249,8 @@ impl<'a, S: Read + Write> BatchHandle<'a, S> { /// Committing an empty batch will make the server queue the callback(s) right away. /// Once committed, the batch can still be re-opened with [open_batch](Producer::open_batch), /// and extra jobs can be added to it. - pub fn commit(self) -> Result<(), Error> { - self.prod.commit_batch(self.bid) + pub async fn commit(self) -> Result<(), Error> { + self.prod.commit_batch(self.bid).await } } @@ -326,11 +332,11 @@ impl<'a> BatchStatus { /// Open the batch for which this `BatchStatus` has been retrieved. /// /// See [`open_batch`](Producer::open_batch). - pub fn open( + pub async fn open( &self, prod: &'a mut Producer, ) -> Result>, Error> { - prod.open_batch(self.bid.clone()) + prod.open_batch(self.bid.clone()).await } } diff --git a/src/proto/mod.rs b/src/proto/mod.rs index f7b6f3ec..0d7fe1a7 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -2,24 +2,18 @@ use crate::{Consumer, Producer}; use crate::error::{self, Error}; -use bufstream::BufStream; -use libc::getpid; use std::io; -use std::io::prelude::*; -use std::net::TcpStream; -use url::Url; - -pub(crate) const EXPECTED_PROTOCOL_VERSION: usize = 2; +use tokio::io::BufStream; +use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt}; +use tokio::net::TcpStream as TokioStream; mod single; - -// commands that users can issue -pub use self::single::{ - Ack, Fail, Heartbeat, Info, Job, JobBuilder, Push, PushBulk, QueueAction, QueueControl, -}; +pub use single::{Ack, Fail, Info, Job, JobBuilder, Push, PushBulk, QueueAction, QueueControl}; +pub(crate) mod utils; #[cfg(feature = "ent")] pub use self::single::ent::{JobState, Progress, ProgressUpdate, ProgressUpdateBuilder, Track}; +use self::single::Heartbeat; #[cfg(feature = "ent")] mod batch; @@ -29,35 +23,7 @@ pub use batch::{ OpenBatch, }; -pub(crate) fn get_env_url() -> String { - use std::env; - let var = env::var("FAKTORY_PROVIDER").unwrap_or_else(|_| "FAKTORY_URL".to_string()); - env::var(var).unwrap_or_else(|_| "tcp://localhost:7419".to_string()) -} - -pub(crate) fn host_from_url(url: &Url) -> String { - format!("{}:{}", url.host_str().unwrap(), url.port().unwrap_or(7419)) -} - -pub(crate) fn url_parse(url: &str) -> Result { - let url = Url::parse(url).map_err(error::Connect::ParseUrl)?; - if url.scheme() != "tcp" { - return Err(error::Connect::BadScheme { - scheme: url.scheme().to_string(), - } - .into()); - } - - if url.host_str().is_none() || url.host_str().unwrap().is_empty() { - return Err(error::Connect::MissingHostname.into()); - } - - Ok(url) -} - -pub(crate) fn parse_provided_or_from_env(url: Option<&str>) -> Result { - url_parse(url.unwrap_or(&get_env_url())) -} +pub(crate) const EXPECTED_PROTOCOL_VERSION: usize = 2; fn check_protocols_match(ver: usize) -> Result<(), Error> { if ver != EXPECTED_PROTOCOL_VERSION { @@ -70,18 +36,6 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { Ok(()) } -/// A stream that can be re-established after failing. -pub trait Reconnect: Sized { - /// Re-establish the stream. - fn reconnect(&self) -> io::Result; -} - -impl Reconnect for TcpStream { - fn reconnect(&self) -> io::Result { - TcpStream::connect(self.peer_addr().unwrap()) - } -} - #[derive(Clone)] pub(crate) struct ClientOptions { /// Hostname to advertise to server. @@ -96,7 +50,7 @@ pub(crate) struct ClientOptions { /// Defaults to a GUID. pub(crate) wid: Option, - /// Labels to advertise to server. + /// Labels to advertise to se/// A stream that can be re-established after failing.rver. /// Defaults to ["rust"]. pub(crate) labels: Vec, @@ -122,6 +76,33 @@ impl Default for ClientOptions { } } +/// A stream that can be re-established after failing. +#[async_trait::async_trait] +pub trait Reconnect: Sized { + /// Re-establish the stream. + async fn reconnect(&mut self) -> io::Result; +} + +#[async_trait::async_trait] +impl Reconnect for TokioStream { + async fn reconnect(&mut self) -> io::Result { + let addr = &self.peer_addr().expect("socket address"); + TokioStream::connect(addr).await + } +} + +#[async_trait::async_trait] +impl Reconnect for BufStream +where + S: AsyncRead + AsyncWrite + Reconnect + Send + Sync, +{ + async fn reconnect(&mut self) -> io::Result { + // let addr = &self.get_ref().peer_addr().expect("socket address"); + let stream = self.get_mut().reconnect().await?; + Ok(Self::new(stream)) + } +} + /// A Faktory connection that represents neither a [`Producer`] nor a [`Consumer`]. /// /// Useful for retrieving and updating information on a job's execution progress @@ -130,10 +111,11 @@ impl Default for ClientOptions { /// /// Fetching a job's execution progress: /// ```no_run +/// # tokio_test::block_on(async { /// use faktory::{Client, ent::JobState}; /// let job_id = String::from("W8qyVle9vXzUWQOf"); -/// let mut cl = Client::connect(None)?; -/// if let Some(progress) = cl.get_progress(job_id)? { +/// let mut cl = Client::connect(None).await?; +/// if let Some(progress) = cl.get_progress(job_id).await? { /// if let JobState::Success = progress.state { /// # /* /// ... @@ -141,39 +123,80 @@ impl Default for ClientOptions { /// } /// } /// # Ok::<(), faktory::Error>(()) +/// }); /// ``` /// /// Sending an update on a job's execution progress: /// /// ```no_run +/// # tokio_test::block_on(async { /// use faktory::{Client, ent::ProgressUpdateBuilder}; /// let jid = String::from("W8qyVle9vXzUWQOf"); -/// let mut cl = Client::connect(None)?; +/// let mut cl = Client::connect(None).await?; /// let progress = ProgressUpdateBuilder::new(&jid) /// .desc("Almost done...".to_owned()) /// .percent(99) /// .build(); -/// cl.set_progress(progress)?; +/// cl.set_progress(progress).await?; /// # Ok::<(), faktory::Error>(()) +/// }); ///```` /// /// Fetching a batch's status: /// /// ```no_run +/// # tokio_test::block_on(async { /// use faktory::Client; /// let bid = String::from("W8qyVle9vXzUWQOg"); -/// let mut cl = Client::connect(None)?; -/// if let Some(status) = cl.get_batch_status(bid)? { +/// let mut cl = Client::connect(None).await?; +/// if let Some(status) = cl.get_batch_status(bid).await? { /// println!("This batch created at {}", status.created_at); /// } /// # Ok::<(), faktory::Error>(()) +/// }); /// ``` -pub struct Client { - stream: BufStream, +pub struct Client { + stream: S, opts: ClientOptions, } -impl Client { +impl Client +where + S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send + Reconnect, +{ + pub(crate) async fn connect_again(&mut self) -> Result { + let s = self.stream.reconnect().await?; + Client::new(s, self.opts.clone()).await + } + + pub(crate) async fn reconnect(&mut self) -> Result<(), Error> { + self.stream = self.stream.reconnect().await?; + self.init().await + } +} + +impl Drop for Client +where + S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, +{ + fn drop(&mut self) { + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + single::write_command(&mut self.stream, &single::End) + .await + .unwrap(); + }) + }); + } +} + +pub(crate) enum HeartbeatStatus { + Ok, + Terminate, + Quiet, +} + +impl Client> { /// Create new [`Client`] and connect to a Faktory server. /// /// If `url` is not given, will use the standard Faktory environment variables. Specifically, @@ -185,53 +208,20 @@ impl Client { /// ```text /// tcp://localhost:7419 /// ``` - pub fn connect(url: Option<&str>) -> Result, Error> { - let url = parse_provided_or_from_env(url)?; - let stream = TcpStream::connect(host_from_url(&url))?; - Self::connect_with(stream, url.password().map(|p| p.to_string())) + pub async fn connect(url: Option<&str>) -> Result>, Error> { + let url = utils::parse_provided_or_from_env(url)?; + let stream = TokioStream::connect(utils::host_from_url(&url)).await?; + let buffered = BufStream::new(stream); + Self::connect_with(buffered, url.password().map(|p| p.to_string())).await } } impl Client where - S: Read + Write + Reconnect, + S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, { - pub(crate) fn connect_again(&self) -> Result { - let s = self.stream.get_ref().reconnect()?; - Client::new(s, self.opts.clone()) - } - - pub(crate) fn reconnect(&mut self) -> Result<(), Error> { - let s = self.stream.get_ref().reconnect()?; - self.stream = BufStream::new(s); - self.init() - } -} - -impl Client { - pub(crate) fn new(stream: S, opts: ClientOptions) -> Result, Error> { - let mut c = Client { - stream: BufStream::new(stream), - opts, - }; - c.init()?; - Ok(c) - } - - /// Create new [`Client`] and connect to a Faktory server with a non-standard stream. - pub fn connect_with(stream: S, pwd: Option) -> Result, Error> { - let opts = ClientOptions { - password: pwd, - ..Default::default() - }; - Client::new(stream, opts) - } -} - -impl Client { - fn init(&mut self) -> Result<(), Error> { - let hi = single::read_hi(&mut self.stream)?; - + async fn init(&mut self) -> Result<(), Error> { + let hi = single::read_hi(&mut self.stream).await?; check_protocols_match(hi.version)?; let mut hello = single::Hello::default(); @@ -245,8 +235,12 @@ impl Client { } } + // fill in any missing options, and remember them for re-connect + let mut hello = single::Hello::default(); + if self.opts.is_worker { // fill in any missing options, and remember them for re-connect + let hostname = self .opts .hostname @@ -254,10 +248,7 @@ impl Client { .or_else(|| hostname::get().ok()?.into_string().ok()) .unwrap_or_else(|| "local".to_string()); self.opts.hostname = Some(hostname); - let pid = self - .opts - .pid - .unwrap_or_else(|| unsafe { getpid() } as usize); + let pid = self.opts.pid.unwrap_or_else(|| std::process::id() as usize); self.opts.pid = Some(pid); let wid = self.opts.wid.clone().unwrap_or_else(single::gen_random_wid); self.opts.wid = Some(wid); @@ -268,62 +259,59 @@ impl Client { hello.labels = self.opts.labels.clone(); } - single::write_command_and_await_ok(&mut self.stream, &hello) - } -} - -impl Drop for Client { - fn drop(&mut self) { - single::write_command(&mut self.stream, &single::End).unwrap(); - } -} + if hi.salt.is_some() { + if let Some(ref pwd) = self.opts.password { + hello.set_password(&hi, pwd); + } else { + return Err(error::Connect::AuthenticationNeeded.into()); + } + } -#[cfg(feature = "ent")] -#[cfg_attr(docsrs, doc(cfg(feature = "ent")))] -impl Client { - /// Send information on a job's execution progress to Faktory. - pub fn set_progress(&mut self, upd: ProgressUpdate) -> Result<(), Error> { - let cmd = Track::Set(upd); - self.issue(&cmd)?.await_ok() + single::write_command_and_await_ok(&mut self.stream, &hello).await?; + Ok(()) } - /// Fetch information on a job's execution progress from Faktory. - pub fn get_progress(&mut self, jid: String) -> Result, Error> { - let cmd = Track::Get(jid); - self.issue(&cmd)?.read_json() + pub(crate) async fn new(stream: S, opts: ClientOptions) -> Result, Error> { + let mut c = Client { stream, opts }; + c.init().await?; + Ok(c) } - /// Fetch information on a batch of jobs execution progress. - pub fn get_batch_status(&mut self, bid: String) -> Result, Error> { - let cmd = GetBatchStatus::from(bid); - self.issue(&cmd)?.read_json() + /// Create new [`Client`] and connect to a Faktory server with a non-standard stream. + pub async fn connect_with(stream: S, pwd: Option) -> Result, Error> { + let opts = ClientOptions { + password: pwd, + ..Default::default() + }; + Client::new(stream, opts).await } -} -pub struct ReadToken<'a, S: Read + Write>(&'a mut Client); - -pub(crate) enum HeartbeatStatus { - Ok, - Terminate, - Quiet, -} - -impl Client { - pub(crate) fn issue( + pub(crate) async fn issue( &mut self, c: &FC, ) -> Result, Error> { - single::write_command(&mut self.stream, c)?; + single::write_command(&mut self.stream, c).await?; Ok(ReadToken(self)) } - pub(crate) fn heartbeat(&mut self) -> Result { + pub(crate) async fn fetch(&mut self, queues: &[Q]) -> Result, Error> + where + Q: AsRef + Sync, + { + self.issue(&single::Fetch::from(queues)) + .await? + .read_json() + .await + } + + pub(crate) async fn heartbeat(&mut self) -> Result { single::write_command( &mut self.stream, &Heartbeat::new(&**self.opts.wid.as_ref().unwrap()), - )?; + ) + .await?; - match single::read_json::<_, serde_json::Value>(&mut self.stream)? { + match single::read_json::<_, serde_json::Value>(&mut self.stream).await? { None => Ok(HeartbeatStatus::Ok), Some(s) => match s .as_object() @@ -340,35 +328,52 @@ impl Client { }, } } +} - pub(crate) fn fetch(&mut self, queues: &[Q]) -> Result, Error> - where - Q: AsRef, - { - self.issue(&single::Fetch::from(queues))?.read_json() +#[cfg(feature = "ent")] +#[cfg_attr(docsrs, doc(cfg(feature = "ent")))] +impl Client { + /// Send information on a job's execution progress to Faktory. + pub async fn set_progress(&mut self, upd: ProgressUpdate) -> Result<(), Error> { + let cmd = Track::Set(upd); + self.issue(&cmd).await?.read_ok().await + } + + /// Fetch information on a job's execution progress from Faktory. + pub async fn get_progress(&mut self, jid: String) -> Result, Error> { + let cmd = Track::Get(jid); + self.issue(&cmd).await?.read_json().await + } + + /// Fetch information on a batch of jobs execution progress. + pub async fn get_batch_status(&mut self, bid: String) -> Result, Error> { + let cmd = GetBatchStatus::from(bid); + self.issue(&cmd).await?.read_json().await } } -impl<'a, S: Read + Write> ReadToken<'a, S> { - pub(crate) fn await_ok(self) -> Result<(), Error> { - single::read_ok(&mut self.0.stream) +pub struct ReadToken<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send>(&'a mut Client); + +impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> ReadToken<'a, S> { + pub(crate) async fn read_ok(self) -> Result<(), Error> { + single::read_ok(&mut self.0.stream).await } - pub(crate) fn read_json(self) -> Result, Error> + pub(crate) async fn read_json(self) -> Result, Error> where T: serde::de::DeserializeOwned, { - single::read_json(&mut self.0.stream) + single::read_json(&mut self.0.stream).await } #[cfg(feature = "ent")] - pub(crate) fn read_bid(self) -> Result { - single::read_bid(&mut self.0.stream) + pub(crate) async fn read_bid(self) -> Result { + single::read_bid(&mut self.0.stream).await } #[cfg(feature = "ent")] - pub(crate) fn maybe_bid(self) -> Result, Error> { - let bid_read_res = single::read_bid(&mut self.0.stream); + pub(crate) async fn maybe_bid(self) -> Result, Error> { + let bid_read_res = single::read_bid(&mut self.0.stream).await; if bid_read_res.is_ok() { return Ok(Some(bid_read_res.unwrap())); } @@ -383,66 +388,3 @@ impl<'a, S: Read + Write> ReadToken<'a, S> { } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - // https://github.com/rust-lang/rust/pull/42219 - //#[allow_fail] - #[ignore] - fn it_works() { - Client::new( - TcpStream::connect("localhost:7419").unwrap(), - ClientOptions::default(), - ) - .unwrap(); - } - - #[test] - fn correct_env_parsing() { - use std::env; - - if env::var_os("FAKTORY_URL").is_some() { - eprintln!("skipping test to avoid messing with user-set FAKTORY_URL"); - return; - } - - assert_eq!(get_env_url(), "tcp://localhost:7419"); - - env::set_var("FAKTORY_URL", "tcp://example.com:7500"); - assert_eq!(get_env_url(), "tcp://example.com:7500"); - - env::set_var("FAKTORY_PROVIDER", "URL"); - env::set_var("URL", "tcp://example.com:7501"); - assert_eq!(get_env_url(), "tcp://example.com:7501"); - } - - #[test] - fn url_port_default() { - use url::Url; - let url = Url::parse("tcp://example.com").unwrap(); - assert_eq!(host_from_url(&url), "example.com:7419"); - } - - #[test] - fn url_requires_tcp() { - url_parse("foobar").unwrap_err(); - } - - #[test] - fn url_requires_host() { - url_parse("tcp://:7419").unwrap_err(); - } - - #[test] - fn url_doesnt_require_port() { - url_parse("tcp://example.com").unwrap(); - } - - #[test] - fn url_can_take_password_and_port() { - url_parse("tcp://:foobar@example.com:7419").unwrap(); - } -} diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index 800d3dd2..ff531e05 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -1,36 +1,54 @@ use crate::{error::Error, Job}; -use std::io::prelude::*; +use std::error::Error as StdError; +use tokio::io::AsyncWriteExt; +#[async_trait::async_trait] pub trait FaktoryCommand { - fn issue(&self, w: &mut W) -> Result<(), Error>; + async fn issue(&self, w: &mut W) -> Result<(), Error>; +} + +macro_rules! self_to_cmd { + ($struct:ident, $cmd:expr) => { + #[async_trait::async_trait] + impl FaktoryCommand for $struct { + async fn issue(&self, w: &mut W) -> Result<(), Error> { + let c = format!("{} ", $cmd); + w.write_all(c.as_bytes()).await?; + let r = serde_json::to_vec(self).map_err(Error::Serialization)?; + w.write_all(&r).await?; + Ok(w.write_all(b"\r\n").await?) + } + } + }; } /// Write queues as part of a command. They are written with a leading space /// followed by space separated queue names. -fn write_queues(w: &mut W, queues: &[S]) -> Result<(), Error> +async fn write_queues(w: &mut W, queues: &[S]) -> Result<(), Error> where - W: Write, + W: AsyncWriteExt + Unpin + Send, S: AsRef, { for q in queues { - w.write_all(b" ")?; - w.write_all(q.as_ref().as_bytes())?; + w.write_all(b" ").await?; + w.write_all(q.as_ref().as_bytes()).await?; } Ok(()) } -// ---------------------------------------------- +// -------------------- INFO ---------------------- pub struct Info; +#[async_trait::async_trait] impl FaktoryCommand for Info { - fn issue(&self, w: &mut W) -> Result<(), Error> { - Ok(w.write_all(b"INFO\r\n")?) + async fn issue(&self, w: &mut W) -> Result<(), Error> { + Ok(w.write_all(b"INFO\r\n").await?) } } -// ---------------------------------------------- +// -------------------- ACK ---------------------- #[derive(Serialize)] pub struct Ack { @@ -38,14 +56,6 @@ pub struct Ack { job_id: String, } -impl FaktoryCommand for Ack { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"ACK ")?; - serde_json::to_writer(&mut *w, self).map_err(Error::Serialization)?; - Ok(w.write_all(b"\r\n")?) - } -} - impl Ack { pub fn new>(job_id: S) -> Ack { Ack { @@ -54,28 +64,24 @@ impl Ack { } } -// ---------------------------------------------- +self_to_cmd!(Ack, "ACK"); + +// -------------------- BEAT ------------------ #[derive(Serialize)] pub struct Heartbeat { wid: String, } -impl FaktoryCommand for Heartbeat { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"BEAT ")?; - serde_json::to_writer(&mut *w, self).map_err(Error::Serialization)?; - Ok(w.write_all(b"\r\n")?) - } -} - impl Heartbeat { pub fn new>(wid: S) -> Heartbeat { Heartbeat { wid: wid.into() } } } -// ---------------------------------------------- +self_to_cmd!(Heartbeat, "BEAT"); + +// -------------------- FAIL --------------------- #[derive(Serialize, Clone)] pub struct Fail { @@ -88,14 +94,6 @@ pub struct Fail { backtrace: Vec, } -impl FaktoryCommand for Fail { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"FAIL ")?; - serde_json::to_writer(&mut *w, self).map_err(Error::Serialization)?; - Ok(w.write_all(b"\r\n")?) - } -} - impl Fail { pub fn new, S2: Into, S3: Into>( job_id: S1, @@ -110,43 +108,62 @@ impl Fail { } } + // "unknown" is the errtype used by the go library too + pub fn generic, S2: Into>(job_id: S1, message: S2) -> Self { + Fail::new(job_id, "unknown", message) + } + pub fn set_backtrace(&mut self, lines: Vec) { self.backtrace = lines; } + + pub fn generic_with_backtrace(jid: String, e: E) -> Self + where + E: StdError, + { + let mut f = Fail::generic(jid, format!("{}", e)); + let mut root = e.source(); + let mut lines = Vec::new(); + while let Some(r) = root.take() { + lines.push(format!("{}", r)); + root = r.source(); + } + f.set_backtrace(lines); + f + } } -// ---------------------------------------------- +self_to_cmd!(Fail, "FAIL"); + +// ---------------------- END -------------------- pub struct End; +#[async_trait::async_trait] impl FaktoryCommand for End { - fn issue(&self, w: &mut W) -> Result<(), Error> { - Ok(w.write_all(b"END\r\n")?) + async fn issue(&self, w: &mut W) -> Result<(), Error> { + Ok(w.write_all(b"END\r\n").await?) } } -// ---------------------------------------------- +// --------------------- FETCH -------------------- pub struct Fetch<'a, S> where S: AsRef, { - queues: &'a [S], + pub(crate) queues: &'a [S], } -impl<'a, S> FaktoryCommand for Fetch<'a, S> +#[async_trait::async_trait] +impl<'a, Q> FaktoryCommand for Fetch<'a, Q> where - S: AsRef, + Q: AsRef + Sync, { - fn issue(&self, w: &mut W) -> Result<(), Error> { - if self.queues.is_empty() { - w.write_all(b"FETCH\r\n")?; - } else { - w.write_all(b"FETCH")?; - write_queues::(w, self.queues)?; - w.write_all(b"\r\n")?; - } - Ok(()) + async fn issue(&self, w: &mut W) -> Result<(), Error> { + w.write_all(b"FETCH").await?; + write_queues(w, self.queues).await?; + Ok(w.write_all(b"\r\n").await?) } } @@ -159,7 +176,7 @@ where } } -// ---------------------------------------------- +// --------------------- HELLO -------------------- #[derive(Serialize)] pub struct Hello { @@ -208,15 +225,9 @@ impl Hello { } } -impl FaktoryCommand for Hello { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"HELLO ")?; - serde_json::to_writer(&mut *w, self).map_err(Error::Serialization)?; - Ok(w.write_all(b"\r\n")?) - } -} +self_to_cmd!(Hello, "HELLO"); -// ---------------------------------------------- +// --------------------- PUSH -------------------- pub struct Push(Job); @@ -234,15 +245,17 @@ impl From for Push { } } +#[async_trait::async_trait] impl FaktoryCommand for Push { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"PUSH ")?; - serde_json::to_writer(&mut *w, &**self).map_err(Error::Serialization)?; - Ok(w.write_all(b"\r\n")?) + async fn issue(&self, w: &mut W) -> Result<(), Error> { + w.write_all(b"PUSH ").await?; + let r = serde_json::to_vec(&**self).map_err(Error::Serialization)?; + w.write_all(&r).await?; + Ok(w.write_all(b"\r\n").await?) } } -// ---------------------------------------------- +// ---------------------- QUEUE ------------------- pub struct PushBulk(Vec); @@ -252,11 +265,13 @@ impl From> for PushBulk { } } +#[async_trait::async_trait] impl FaktoryCommand for PushBulk { - fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"PUSHB ")?; - serde_json::to_writer(&mut *w, &self.0).map_err(Error::Serialization)?; - Ok(w.write_all(b"\r\n")?) + async fn issue(&self, w: &mut W) -> Result<(), Error> { + w.write_all(b"PUSHB ").await?; + let r = serde_json::to_vec(&self.0).map_err(Error::Serialization)?; + w.write_all(&r).await?; + Ok(w.write_all(b"\r\n").await?) } } @@ -275,19 +290,21 @@ where pub queues: &'a [S], } -impl> FaktoryCommand for QueueControl<'_, S> { - fn issue(&self, w: &mut W) -> Result<(), Error> { +#[async_trait::async_trait] +impl FaktoryCommand for QueueControl<'_, Q> +where + Q: AsRef + Sync, +{ + async fn issue(&self, w: &mut W) -> Result<(), Error> { let command = match self.action { QueueAction::Pause => b"QUEUE PAUSE".as_ref(), QueueAction::Resume => b"QUEUE RESUME".as_ref(), }; - - w.write_all(command)?; - write_queues::(w, self.queues)?; - Ok(w.write_all(b"\r\n")?) + w.write_all(command).await?; + write_queues(w, self.queues).await?; + Ok(w.write_all(b"\r\n").await?) } } - impl<'a, S: AsRef> QueueControl<'a, S> { pub fn new(action: QueueAction, queues: &'a [S]) -> Self { Self { action, queues } diff --git a/src/proto/single/ent/cmd.rs b/src/proto/single/ent/cmd.rs index 50d06b81..b522bb89 100644 --- a/src/proto/single/ent/cmd.rs +++ b/src/proto/single/ent/cmd.rs @@ -1,7 +1,7 @@ use super::ProgressUpdate; use crate::error::Error; use crate::proto::single::FaktoryCommand; -use std::{fmt::Debug, io::Write}; +use tokio::io::AsyncWriteExt; #[derive(Debug, Clone)] pub enum Track { @@ -9,18 +9,20 @@ pub enum Track { Get(String), } +#[async_trait::async_trait] impl FaktoryCommand for Track { - fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { match self { Self::Set(upd) => { - w.write_all(b"TRACK SET ")?; - serde_json::to_writer(&mut *w, upd).map_err(Error::Serialization)?; - Ok(w.write_all(b"\r\n")?) + w.write_all(b"TRACK SET ").await?; + let r = serde_json::to_vec(upd).map_err(Error::Serialization)?; + w.write_all(&r).await?; + Ok(w.write_all(b"\r\n").await?) } Self::Get(jid) => { - w.write_all(b"TRACK GET ")?; - w.write_all(jid.as_bytes())?; - Ok(w.write_all(b"\r\n")?) + w.write_all(b"TRACK GET ").await?; + w.write_all(jid.as_bytes()).await?; + Ok(w.write_all(b"\r\n").await?) } } } diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index b2607347..6fdc3a86 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -1,7 +1,8 @@ +use crate::Error; use chrono::{DateTime, Utc}; use derive_builder::Builder; use std::collections::HashMap; -use std::io::prelude::*; +use tokio::io::{AsyncBufRead, AsyncWriteExt}; mod cmd; mod resp; @@ -13,7 +14,6 @@ pub mod ent; pub use self::cmd::*; pub use self::resp::*; -use crate::error::Error; pub(crate) use self::utils::gen_random_wid; @@ -255,18 +255,23 @@ impl Job { &self.failure } } - -pub fn write_command(w: &mut W, command: &C) -> Result<(), Error> { - command.issue::(w)?; - Ok(w.flush()?) +pub async fn write_command( + w: &mut W, + command: &C, +) -> Result<(), Error> { + command.issue::(w).await?; + Ok(w.flush().await?) } -pub fn write_command_and_await_ok( - x: &mut X, +pub async fn write_command_and_await_ok< + S: AsyncBufRead + AsyncWriteExt + Unpin + Send, + C: FaktoryCommand, +>( + stream: &mut S, command: &C, ) -> Result<(), Error> { - write_command(x, command)?; - read_ok(x) + write_command(stream, command).await?; + read_ok(stream).await } #[cfg(test)] diff --git a/src/proto/single/resp.rs b/src/proto/single/resp.rs index 9a1d3e19..095c5511 100644 --- a/src/proto/single/resp.rs +++ b/src/proto/single/resp.rs @@ -1,7 +1,7 @@ use crate::error::{self, Error}; -use std::io::prelude::*; +use tokio::io::{AsyncBufReadExt, AsyncReadExt}; -fn bad(expected: &'static str, got: &RawResponse) -> error::Protocol { +pub fn bad(expected: &'static str, got: &RawResponse) -> error::Protocol { let stringy = match *got { RawResponse::String(ref s) => Some(&**s), RawResponse::Blob(ref b) => { @@ -28,8 +28,10 @@ fn bad(expected: &'static str, got: &RawResponse) -> error::Protocol { // ---------------------------------------------- -pub fn read_json(r: R) -> Result, Error> { - let rr = read(r)?; +pub async fn read_json( + r: R, +) -> Result, Error> { + let rr = read(r).await?; match rr { RawResponse::String(ref s) if s == "OK" => { return Ok(None); @@ -60,8 +62,8 @@ pub fn read_json(r: R) -> Result(r: R) -> Result { - match read(r)? { +pub async fn read_bid(r: R) -> Result { + match read(r).await? { RawResponse::Blob(ref b) if b.is_empty() => Err(error::Protocol::BadType { expected: "non-empty blob representation of batch id", received: "empty blob".into(), @@ -89,21 +91,20 @@ pub struct Hi { pub salt: Option, } -pub fn read_hi(r: R) -> Result { - let rr = read(r)?; +pub async fn read_hi(r: R) -> Result { + let rr = read(r).await?; if let RawResponse::String(ref s) = rr { if let Some(s) = s.strip_prefix("HI ") { return serde_json::from_str(s).map_err(Error::Serialization); } } - Err(bad("server hi", &rr).into()) } // ---------------------------------------------- -pub fn read_ok(r: R) -> Result<(), Error> { - let rr = read(r)?; +pub async fn read_ok(r: R) -> Result<(), Error> { + let rr = read(r).await?; if let RawResponse::String(ref s) = rr { if s == "OK" { return Ok(()); @@ -120,22 +121,25 @@ pub fn read_ok(r: R) -> Result<(), Error> { // ---------------------------------------------- #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -enum RawResponse { +pub enum RawResponse { String(String), Blob(Vec), Number(isize), Null, } -fn read(mut r: R) -> Result { +async fn read(mut r: R) -> Result +where + R: AsyncReadExt + AsyncBufReadExt + Unpin, +{ let mut cmdbuf = [0u8; 1]; - r.read_exact(&mut cmdbuf)?; + r.read_exact(&mut cmdbuf).await?; match cmdbuf[0] { b'+' => { // Simple String // https://redis.io/topics/protocol#resp-simple-strings let mut s = String::new(); - r.read_line(&mut s)?; + r.read_line(&mut s).await?; // remove newlines let l = s.len() - 2; @@ -147,7 +151,7 @@ fn read(mut r: R) -> Result { // Error // https://redis.io/topics/protocol#resp-errors let mut s = String::new(); - r.read_line(&mut s)?; + r.read_line(&mut s).await?; // remove newlines let l = s.len() - 2; @@ -159,7 +163,7 @@ fn read(mut r: R) -> Result { // Integer // https://redis.io/topics/protocol#resp-integers let mut s = String::with_capacity(32); - r.read_line(&mut s)?; + r.read_line(&mut s).await?; // remove newlines let l = s.len() - 2; @@ -179,7 +183,7 @@ fn read(mut r: R) -> Result { // Bulk String // https://redis.io/topics/protocol#resp-bulk-strings let mut bytes = Vec::with_capacity(32); - r.read_until(b'\n', &mut bytes)?; + r.read_until(b'\n', &mut bytes).await?; let s = std::str::from_utf8(&bytes[0..bytes.len() - 2]).map_err(|_| { error::Protocol::BadResponse { typed_as: "bulk string", @@ -201,8 +205,8 @@ fn read(mut r: R) -> Result { } else { let size = size as usize; let mut bytes = vec![0; size]; - r.read_exact(&mut bytes[..])?; - r.read_exact(&mut [0u8; 2])?; + r.read_exact(&mut bytes[..]).await?; + r.read_exact(&mut [0u8; 2]).await?; Ok(RawResponse::Blob(bytes)) } } @@ -248,32 +252,34 @@ impl From> for RawResponse { #[cfg(test)] mod test { use super::{read, RawResponse}; + use crate::error::{self, Error}; use serde_json::{self, Map, Value}; - use std::io::{self, Cursor}; + use std::io::Cursor; + use tokio::io::AsyncBufReadExt; - fn read_json(c: C) -> Result, Error> { - super::read_json(c) + async fn read_json(c: C) -> Result, Error> { + super::read_json(c).await } - #[test] - fn it_parses_simple_strings() { + #[tokio::test] + async fn it_parses_simple_strings() { let c = Cursor::new(b"+OK\r\n"); - assert_eq!(read(c).unwrap(), RawResponse::from("OK")); + assert_eq!(read(c).await.unwrap(), RawResponse::from("OK")); } - #[test] - fn it_parses_numbers() { + #[tokio::test] + async fn it_parses_numbers() { let c = Cursor::new(b":1024\r\n"); - assert_eq!(read(c).unwrap(), RawResponse::from(1024)); + assert_eq!(read(c).await.unwrap(), RawResponse::from(1024)); } - #[test] - fn it_errors_on_bad_numbers() { + #[tokio::test] + async fn it_errors_on_bad_numbers() { let c = Cursor::new(b":x\r\n"); if let Error::Protocol(error::Protocol::BadResponse { typed_as, error, .. - }) = read(c).unwrap_err() + }) = read(c).await.unwrap_err() { assert_eq!(typed_as, "integer"); assert_eq!(error, "invalid integer value"); @@ -282,35 +288,35 @@ mod test { } } - #[test] - fn it_parses_errors() { + #[tokio::test] + async fn it_parses_errors() { let c = Cursor::new(b"-ERR foo\r\n"); - if let Error::Protocol(error::Protocol::Internal { ref msg }) = read(c).unwrap_err() { + if let Error::Protocol(error::Protocol::Internal { ref msg }) = read(c).await.unwrap_err() { assert_eq!(msg, "foo"); } else { unreachable!(); } } - #[test] + #[tokio::test] #[should_panic] - fn it_cant_do_arrays() { + async fn it_cant_do_arrays() { let c = Cursor::new(b"*\r\n"); - read(c).unwrap_err(); + read(c).await.unwrap_err(); } - #[test] - fn it_parses_nills() { + #[tokio::test] + async fn it_parses_nills() { let c = Cursor::new(b"$-1\r\n"); - assert_eq!(read(c).unwrap(), RawResponse::Null); + assert_eq!(read(c).await.unwrap(), RawResponse::Null); } - #[test] - fn it_errors_on_bad_sizes() { + #[tokio::test] + async fn it_errors_on_bad_sizes() { let c = Cursor::new(b"$x\r\n\r\n"); if let Error::Protocol(error::Protocol::BadResponse { typed_as, error, .. - }) = read(c).unwrap_err() + }) = read(c).await.unwrap_err() { assert_eq!(typed_as, "bulk string"); assert_eq!(error, "server bulk response size prefix is not an integer"); @@ -319,88 +325,88 @@ mod test { } } - #[test] - fn it_parses_empty_bulk() { + #[tokio::test] + async fn it_parses_empty_bulk() { let c = Cursor::new(b"$0\r\n\r\n"); - assert_eq!(read(c).unwrap(), RawResponse::from(vec![])); + assert_eq!(read(c).await.unwrap(), RawResponse::from(vec![])); } - #[test] - fn it_parses_non_empty_bulk() { + #[tokio::test] + async fn it_parses_non_empty_bulk() { let c = Cursor::new(b"$11\r\nHELLO WORLD\r\n"); assert_eq!( - read(c).unwrap(), + read(c).await.unwrap(), RawResponse::from(Vec::from(&b"HELLO WORLD"[..])) ); } - #[test] - fn it_decodes_json_ok_string() { + #[tokio::test] + async fn it_decodes_json_ok_string() { let c = Cursor::new(b"+OK\r\n"); - assert_eq!(read_json(c).unwrap(), None); + assert_eq!(read_json(c).await.unwrap(), None); } - #[test] - fn it_decodes_json_ok_blob() { + #[tokio::test] + async fn it_decodes_json_ok_blob() { let c = Cursor::new(b"$2\r\nOK\r\n"); - assert_eq!(read_json(c).unwrap(), None); + assert_eq!(read_json(c).await.unwrap(), None); } - #[test] - fn it_decodes_json_nill() { + #[tokio::test] + async fn it_decodes_json_nill() { let c = Cursor::new(b"$-1\r\n"); - assert_eq!(read_json(c).unwrap(), None); + assert_eq!(read_json(c).await.unwrap(), None); } - #[test] - fn it_decodes_json_empty() { + #[tokio::test] + async fn it_decodes_json_empty() { let c = Cursor::new(b"$0\r\n\r\n"); - assert_eq!(read_json(c).unwrap(), None); + assert_eq!(read_json(c).await.unwrap(), None); } - #[test] - fn it_decodes_string_json() { + #[tokio::test] + async fn it_decodes_string_json() { let c = Cursor::new(b"+{\"hello\":1}\r\n"); let mut m = Map::new(); m.insert("hello".to_string(), Value::from(1)); - assert_eq!(read_json(c).unwrap(), Some(Value::Object(m))); + assert_eq!(read_json(c).await.unwrap(), Some(Value::Object(m))); } - #[test] - fn it_decodes_blob_json() { + #[tokio::test] + async fn it_decodes_blob_json() { let c = Cursor::new(b"$11\r\n{\"hello\":1}\r\n"); let mut m = Map::new(); m.insert("hello".to_string(), Value::from(1)); - assert_eq!(read_json(c).unwrap(), Some(Value::Object(m))); + assert_eq!(read_json(c).await.unwrap(), Some(Value::Object(m))); } - #[test] - fn it_errors_on_bad_json_blob() { + #[tokio::test] + async fn it_errors_on_bad_json_blob() { let c = Cursor::new(b"$9\r\n{\"hello\"}\r\n"); - if let Error::Serialization(err) = read_json(c).unwrap_err() { + if let Error::Serialization(err) = read_json(c).await.unwrap_err() { let _: serde_json::Error = err; } else { unreachable!(); } } - #[test] - fn it_errors_on_bad_json_string() { + #[tokio::test] + async fn it_errors_on_bad_json_string() { let c = Cursor::new(b"+{\"hello\"}\r\n"); - if let Error::Serialization(err) = read_json(c).unwrap_err() { + if let Error::Serialization(err) = read_json(c).await.unwrap_err() { let _: serde_json::Error = err; } else { unreachable!(); } } - #[test] - fn json_error_on_number() { + #[tokio::test] + async fn json_error_on_number() { let c = Cursor::new(b":9\r\n"); if let Error::Protocol(error::Protocol::BadType { expected, ref received, - }) = read_json(c).unwrap_err() + }) = read_json(c).await.unwrap_err() { assert_eq!(expected, "json"); assert_eq!(received, "Number(9)"); @@ -409,12 +415,12 @@ mod test { } } - #[test] - fn it_errors_on_unknown_resp_type() { + #[tokio::test] + async fn it_errors_on_unknown_resp_type() { let c = Cursor::new(b"^\r\n"); if let Error::Protocol(error::Protocol::BadResponse { typed_as, error, .. - }) = read_json(c).unwrap_err() + }) = read_json(c).await.unwrap_err() { assert_eq!(typed_as, "unknown"); assert_eq!(error, "invalid response type prefix"); diff --git a/src/proto/utils.rs b/src/proto/utils.rs new file mode 100644 index 00000000..f2ac84db --- /dev/null +++ b/src/proto/utils.rs @@ -0,0 +1,83 @@ +use crate::error::{self, Error}; +use url::Url; + +pub(crate) fn get_env_url() -> String { + use std::env; + let var = env::var("FAKTORY_PROVIDER").unwrap_or_else(|_| "FAKTORY_URL".to_string()); + env::var(var).unwrap_or_else(|_| "tcp://localhost:7419".to_string()) +} + +pub(crate) fn host_from_url(url: &Url) -> String { + format!("{}:{}", url.host_str().unwrap(), url.port().unwrap_or(7419)) +} + +pub(crate) fn url_parse(url: &str) -> Result { + let url = Url::parse(url).map_err(error::Connect::ParseUrl)?; + if url.scheme() != "tcp" { + return Err(error::Connect::BadScheme { + scheme: url.scheme().to_string(), + } + .into()); + } + + if url.host_str().is_none() || url.host_str().unwrap().is_empty() { + return Err(error::Connect::MissingHostname.into()); + } + + Ok(url) +} + +pub(crate) fn parse_provided_or_from_env(url: Option<&str>) -> Result { + url_parse(url.unwrap_or(&get_env_url())) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn correct_env_parsing() { + use std::env; + + if env::var_os("FAKTORY_URL").is_some() { + eprintln!("skipping test to avoid messing with user-set FAKTORY_URL"); + return; + } + + assert_eq!(get_env_url(), "tcp://localhost:7419"); + + env::set_var("FAKTORY_URL", "tcp://example.com:7500"); + assert_eq!(get_env_url(), "tcp://example.com:7500"); + + env::set_var("FAKTORY_PROVIDER", "URL"); + env::set_var("URL", "tcp://example.com:7501"); + assert_eq!(get_env_url(), "tcp://example.com:7501"); + } + + #[test] + fn url_port_default() { + use url::Url; + let url = Url::parse("tcp://example.com").unwrap(); + assert_eq!(host_from_url(&url), "example.com:7419"); + } + + #[test] + fn url_requires_tcp() { + url_parse("foobar").unwrap_err(); + } + + #[test] + fn url_requires_host() { + url_parse("tcp://:7419").unwrap_err(); + } + + #[test] + fn url_doesnt_require_port() { + url_parse("tcp://example.com").unwrap(); + } + + #[test] + fn url_can_take_password_and_port() { + url_parse("tcp://:foobar@example.com:7419").unwrap(); + } +} diff --git a/src/tls.rs b/src/tls.rs index f9db6ca4..30576140 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,12 +1,15 @@ -use crate::proto::{self, Reconnect}; -use crate::Error; -use native_tls::TlsConnector; -use native_tls::TlsStream as NativeTlsStream; +use crate::{proto::utils, Error, Reconnect}; +use std::fmt::Debug; use std::io; -use std::io::prelude::*; -use std::net::TcpStream; +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::net::TcpStream as TokioTcpStream; +use tokio_rustls::client::TlsStream as UnderlyingTlsStream; +use tokio_rustls::rustls::{ClientConfig, RootCertStore}; +use tokio_rustls::TlsConnector; -/// A reconnectable stream encrypted with TLS. +/// A reconnectable asynchronous stream encrypted with TLS. /// /// This can be used as an argument to `Consumer::connect_with` and `Producer::connect_with` to /// connect to a TLS-secured Faktory server. @@ -14,19 +17,23 @@ use std::net::TcpStream; /// # Examples /// /// ```no_run +/// # tokio_test::block_on(async { /// use faktory::{Producer, TlsStream}; -/// let tls = TlsStream::connect(None).unwrap(); -/// let p = Producer::connect_with(tls, None).unwrap(); +/// let tls = TlsStream::connect(None).await.unwrap(); +/// let p = Producer::connect_with(tls, None).await.unwrap(); /// # drop(p); +/// # }); /// ``` /// +#[pin_project::pin_project] pub struct TlsStream { connector: TlsConnector, - hostname: String, - stream: NativeTlsStream, + hostname: &'static str, + #[pin] + stream: UnderlyingTlsStream, } -impl TlsStream { +impl TlsStream { /// Create a new TLS connection over TCP. /// /// If `url` is not given, will use the standard Faktory environment variables. Specifically, @@ -40,88 +47,132 @@ impl TlsStream { /// ``` /// /// If `url` is given, but does not specify a port, it defaults to 7419. - pub fn connect(url: Option<&str>) -> Result { - TlsStream::with_connector( - TlsConnector::builder().build().map_err(Error::TlsStream)?, - url, - ) + /// + /// Internally creates a `ClientConfig` with an empty root certificates store and no client + /// authentication. Use [`with_client_config`](TlsStream::with_client_config) + /// or [`with_connector`](TlsStream::with_connector) for customized + /// `ClientConfig` and `TlsConnector` accordingly. + pub async fn connect(url: Option<&str>) -> Result { + let conf = ClientConfig::builder() + .with_root_certificates(RootCertStore::empty()) + .with_no_client_auth(); + let con = TlsConnector::from(Arc::new(conf)); + TlsStream::with_connector(con, url).await + } + + /// Create a new asynchronous TLS connection over TCP using a non-default TLS configuration. + /// + /// See `connect` for details about the `url` parameter. + pub async fn with_client_config(conf: ClientConfig, url: Option<&str>) -> Result { + let con = TlsConnector::from(Arc::new(conf)); + TlsStream::with_connector(con, url).await } - /// Create a new TLS connection over TCP using a non-default TLS configuration. + /// Create a new asynchronous TLS connection over TCP using a connector with a non-default TLS configuration. /// /// See `connect` for details about the `url` parameter. - pub fn with_connector(tls: TlsConnector, url: Option<&str>) -> Result { + pub async fn with_connector(connector: TlsConnector, url: Option<&str>) -> Result { let url = match url { - Some(url) => proto::url_parse(url), - None => proto::url_parse(&proto::get_env_url()), + Some(url) => utils::url_parse(url), + None => utils::url_parse(&utils::get_env_url()), }?; - let stream = TcpStream::connect(proto::host_from_url(&url))?; - Ok(TlsStream::new(stream, tls, url.host_str().unwrap())?) + let hostname = utils::host_from_url(&url); + let tcp_stream = TokioTcpStream::connect(&hostname).await?; + let hostname: &'static str = url.host_str().unwrap().to_string().leak(); + Ok(TlsStream::new(tcp_stream, connector, hostname).await?) } } -use std::fmt::Debug; impl TlsStream where - S: Read + Write + Reconnect + Send + Sync + Debug + 'static, + S: AsyncRead + AsyncWrite + Send + Unpin + Reconnect + Debug + 'static, { - /// Create a new TLS connection on an existing stream. - pub fn default(stream: S, hostname: &str) -> io::Result { - Self::new(stream, TlsConnector::builder().build().unwrap(), hostname) + /// Create a new asynchronous TLS connection on an existing stream. + /// + /// Internally creates a `ClientConfig` with an empty root certificates store and no client + /// authentication. Use [`new`](TlsStream::new) for a customized `TlsConnector`. + pub async fn default(stream: S, hostname: &'static str) -> io::Result { + let conf = ClientConfig::builder() + .with_root_certificates(RootCertStore::empty()) + .with_no_client_auth(); + + Self::new(stream, TlsConnector::from(Arc::new(conf)), hostname).await } - /// Create a new TLS connection on an existing stream with a non-default TLS configuration. - pub fn new(stream: S, tls: TlsConnector, hostname: &str) -> io::Result { - let stream = tls - .connect(hostname, stream) + /// Create a new asynchronous TLS connection on an existing stream with a non-default TLS configuration. + pub async fn new( + stream: S, + connector: TlsConnector, + hostname: &'static str, + ) -> io::Result { + // let hostname: &'static str = hostname.to_string().leak(); + let domain = hostname.try_into().expect("a valid DNS name or IP address"); + let tls_stream = connector + .connect(domain, stream) + .await .map_err(|e| io::Error::new(io::ErrorKind::ConnectionAborted, e))?; - Ok(TlsStream { - connector: tls, - hostname: hostname.to_string(), - stream, + connector, + hostname, + stream: tls_stream, }) } } +#[async_trait::async_trait] impl Reconnect for TlsStream where - S: Read + Write + Reconnect + Send + Sync + Debug + 'static, + S: AsyncRead + AsyncWrite + Send + Unpin + Reconnect + Debug + 'static + Sync, { - fn reconnect(&self) -> io::Result { - Self::new( - self.stream.get_ref().reconnect()?, - self.connector.clone(), - &self.hostname, - ) + async fn reconnect(&mut self) -> io::Result { + let stream = self.stream.get_mut().0.reconnect().await?; + Self::new(stream, self.connector.clone(), &self.hostname).await } } -use std::ops::{Deref, DerefMut}; impl Deref for TlsStream { - type Target = NativeTlsStream; + type Target = UnderlyingTlsStream; fn deref(&self) -> &Self::Target { &self.stream } } + impl DerefMut for TlsStream { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.stream } } -impl Read for TlsStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.stream.read(buf) +impl AsyncRead for TlsStream { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + self.project().stream.poll_read(cx, buf) } } -impl Write for TlsStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.stream.write(buf) +impl AsyncWrite for TlsStream { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + self.project().stream.poll_write(cx, buf) + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.project().stream.poll_flush(cx) } - fn flush(&mut self) -> io::Result<()> { - self.stream.flush() + fn poll_shutdown( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.project().stream.poll_shutdown(cx) } } diff --git a/tests/consumer.rs b/tests/consumer.rs index ee7c4aea..3505dcac 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -1,25 +1,22 @@ extern crate faktory; -extern crate mockstream; extern crate serde_json; extern crate url; mod mock; use faktory::*; -use std::io; -use std::thread; -use std::time::Duration; +use std::{io, time::Duration}; +use tokio::{spawn, time::sleep}; -#[test] -fn hello() { +#[tokio::test(flavor = "multi_thread")] +async fn hello() { let mut s = mock::Stream::default(); - - let mut c = ConsumerBuilder::default(); + let mut c: ConsumerBuilder = ConsumerBuilder::default(); c.hostname("host".to_string()) .wid("wid".to_string()) .labels(vec!["foo".to_string(), "bar".to_string()]); - c.register("never_called", |_| -> io::Result<()> { unreachable!() }); - let c = c.connect_with(s.clone(), None).unwrap(); + c.register("never_called", |_| Box::pin(async move { unreachable!() })); + let c = c.connect_with(s.clone(), None).await.unwrap(); let written = s.pop_bytes_written(0); assert!(written.starts_with(b"HELLO {")); let written: serde_json::Value = serde_json::from_slice(&written[b"HELLO ".len()..]).unwrap(); @@ -39,14 +36,15 @@ fn hello() { assert_eq!(written, b"END\r\n"); } -#[test] -fn hello_pwd() { +#[tokio::test(flavor = "multi_thread")] +async fn hello_pwd() { let mut s = mock::Stream::with_salt(1545, "55104dc76695721d"); - let mut c = ConsumerBuilder::default(); - c.register("never_called", |_| -> io::Result<()> { unreachable!() }); + let mut c: ConsumerBuilder = ConsumerBuilder::default(); + c.register("never_called", |_| Box::pin(async move { unreachable!() })); let c = c .connect_with(s.clone(), Some("foobar".to_string())) + .await .unwrap(); let written = s.pop_bytes_written(0); assert!(written.starts_with(b"HELLO {")); @@ -60,15 +58,17 @@ fn hello_pwd() { drop(c); } -#[test] -fn dequeue() { +#[tokio::test(flavor = "multi_thread")] +async fn dequeue() { let mut s = mock::Stream::default(); let mut c = ConsumerBuilder::default(); - c.register("foobar", |job: Job| -> io::Result<()> { - assert_eq!(job.args(), &["z"]); - Ok(()) + c.register("foobar", |job: Job| { + Box::pin(async move { + assert_eq!(job.args(), &["z"]); + Ok::<(), io::Error>(()) + }) }); - let mut c = c.connect_with(s.clone(), None).unwrap(); + let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); s.push_bytes_to_read( @@ -86,7 +86,7 @@ fn dequeue() { }\r\n", ); s.ok(0); // for the ACK - if let Err(e) = c.run_one(0, &["default"]) { + if let Err(e) = c.run_one(0, &["default"]).await { println!("{:?}", e); unreachable!(); } @@ -99,15 +99,17 @@ fn dequeue() { ); } -#[test] -fn dequeue_first_empty() { +#[tokio::test(flavor = "multi_thread")] +async fn dequeue_first_empty() { let mut s = mock::Stream::default(); let mut c = ConsumerBuilder::default(); - c.register("foobar", |job: Job| -> io::Result<()> { - assert_eq!(job.args(), &["z"]); - Ok(()) + c.register("foobar", |job: Job| { + Box::pin(async move { + assert_eq!(job.args(), &["z"]); + Ok::<(), io::Error>(()) + }) }); - let mut c = c.connect_with(s.clone(), None).unwrap(); + let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); s.push_bytes_to_read( @@ -127,7 +129,7 @@ fn dequeue_first_empty() { s.ok(0); // for the ACK // run once, shouldn't do anything - match c.run_one(0, &["default"]) { + match c.run_one(0, &["default"]).await { Ok(did_work) => assert!(!did_work), Err(e) => { println!("{:?}", e); @@ -135,7 +137,7 @@ fn dequeue_first_empty() { } } // run again, this time doing the job - match c.run_one(0, &["default"]) { + match c.run_one(0, &["default"]).await { Ok(did_work) => assert!(did_work), Err(e) => { println!("{:?}", e); @@ -154,17 +156,19 @@ fn dequeue_first_empty() { ); } -#[test] -fn well_behaved() { +#[tokio::test(flavor = "multi_thread")] +async fn well_behaved() { let mut s = mock::Stream::new(2); // main plus worker let mut c = ConsumerBuilder::default(); c.wid("wid".to_string()); - c.register("foobar", |_| -> io::Result<()> { - // NOTE: this time needs to be so that it lands between the first heartbeat and the second - thread::sleep(Duration::from_secs(7)); - Ok(()) + c.register("foobar", |_| { + Box::pin(async move { + // NOTE: this time needs to be so that it lands between the first heartbeat and the second + sleep(Duration::from_secs(7)).await; + Ok::<(), io::Error>(()) + }) }); - let mut c = c.connect_with(s.clone(), None).unwrap(); + let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); // push a job that'll take a while to run @@ -183,7 +187,7 @@ fn well_behaved() { }\r\n", ); - let jh = thread::spawn(move || c.run(&["default"])); + let jh = spawn(async move { c.run(&["default"]).await }); // the running thread won't return for a while. the heartbeat thingy is going to eventually // send a heartbeat, and we want to respond to that with a "quiet" to make it not accept any @@ -197,7 +201,7 @@ fn well_behaved() { s.push_bytes_to_read(0, b"+{\"state\":\"terminate\"}\r\n"); // at this point, c.run() should eventually return with Ok(0) indicating that it finished. - assert_eq!(jh.join().unwrap().unwrap(), 0); + assert_eq!(jh.await.unwrap().unwrap(), 0); // heartbeat should have seen two beats (quiet + terminate) let written = s.pop_bytes_written(0); @@ -219,17 +223,19 @@ fn well_behaved() { ); } -#[test] -fn no_first_job() { +#[tokio::test(flavor = "multi_thread")] +async fn no_first_job() { let mut s = mock::Stream::new(2); let mut c = ConsumerBuilder::default(); c.wid("wid".to_string()); - c.register("foobar", |_| -> io::Result<()> { - // NOTE: this time needs to be so that it lands between the first heartbeat and the second - thread::sleep(Duration::from_secs(7)); - Ok(()) + c.register("foobar", |_| { + Box::pin(async move { + // NOTE: this time needs to be so that it lands between the first heartbeat and the second + sleep(Duration::from_secs(7)).await; + Ok::<(), io::Error>(()) + }) }); - let mut c = c.connect_with(s.clone(), None).unwrap(); + let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); // push a job that'll take a while to run @@ -248,7 +254,7 @@ fn no_first_job() { }\r\n", ); - let jh = thread::spawn(move || c.run(&["default"])); + let jh = spawn(async move { c.run(&["default"]).await }); // the running thread won't return for a while. the heartbeat thingy is going to eventually // send a heartbeat, and we want to respond to that with a "quiet" to make it not accept any @@ -262,7 +268,7 @@ fn no_first_job() { s.push_bytes_to_read(0, b"+{\"state\":\"terminate\"}\r\n"); // at this point, c.run() should eventually return with Ok(0) indicating that it finished. - assert_eq!(jh.join().unwrap().unwrap(), 0); + assert_eq!(jh.await.unwrap().unwrap(), 0); // heartbeat should have seen two beats (quiet + terminate) let written = s.pop_bytes_written(0); @@ -285,18 +291,20 @@ fn no_first_job() { ); } -#[test] -fn well_behaved_many() { +#[tokio::test(flavor = "multi_thread")] +async fn well_behaved_many() { let mut s = mock::Stream::new(3); let mut c = ConsumerBuilder::default(); c.workers(2); c.wid("wid".to_string()); - c.register("foobar", |_| -> io::Result<()> { - // NOTE: this time needs to be so that it lands between the first heartbeat and the second - thread::sleep(Duration::from_secs(7)); - Ok(()) + c.register("foobar", |_| { + Box::pin(async move { + // NOTE: this time needs to be so that it lands between the first heartbeat and the second + sleep(Duration::from_secs(7)).await; + Ok::<(), io::Error>(()) + }) }); - let mut c = c.connect_with(s.clone(), None).unwrap(); + let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); // push two jobs that'll take a while to run @@ -322,7 +330,7 @@ fn well_behaved_many() { ); } - let jh = thread::spawn(move || c.run(&["default"])); + let jh = spawn(async move { c.run(&["default"]).await }); // the running thread won't return for a while. the heartbeat thingy is going to eventually // send a heartbeat, and we want to respond to that with a "quiet" to make it not accept any @@ -337,7 +345,7 @@ fn well_behaved_many() { s.push_bytes_to_read(0, b"+{\"state\":\"terminate\"}\r\n"); // at this point, c.run() should eventually return with Ok(0) indicating that it finished. - assert_eq!(jh.join().unwrap().unwrap(), 0); + assert_eq!(jh.await.unwrap().unwrap(), 0); // heartbeat should have seen two beats (quiet + terminate) let written = s.pop_bytes_written(0); @@ -361,17 +369,19 @@ fn well_behaved_many() { } } -#[test] -fn terminate() { +#[tokio::test(flavor = "multi_thread")] +async fn terminate() { let mut s = mock::Stream::new(2); - let mut c = ConsumerBuilder::default(); + let mut c: ConsumerBuilder = ConsumerBuilder::default(); c.wid("wid".to_string()); - c.register("foobar", |_| -> io::Result<()> { - loop { - thread::sleep(Duration::from_secs(5)); - } + c.register("foobar", |_| { + Box::pin(async move { + loop { + sleep(Duration::from_secs(5)).await; + } + }) }); - let mut c = c.connect_with(s.clone(), None).unwrap(); + let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); s.push_bytes_to_read( @@ -389,7 +399,7 @@ fn terminate() { }\r\n", ); - let jh = thread::spawn(move || c.run(&["default"])); + let jh = spawn(async move { c.run(&["default"]).await }); // the running thread won't ever return, because the job never exits. the heartbeat thingy is // going to eventually send a heartbeat, and we want to respond to that with a "terminate" @@ -397,7 +407,7 @@ fn terminate() { // at this point, c.run() should immediately return with Ok(1) indicating that one job is still // running. - assert_eq!(jh.join().unwrap().unwrap(), 1); + assert_eq!(jh.await.unwrap().unwrap(), 1); // heartbeat should have seen one beat (terminate) and then send FAIL let written = s.pop_bytes_written(0); diff --git a/tests/mock/inner.rs b/tests/mock/inner.rs new file mode 100644 index 00000000..b7fd2272 --- /dev/null +++ b/tests/mock/inner.rs @@ -0,0 +1,39 @@ +use std::sync::{Arc, Mutex}; +use std::{io, mem}; + +#[derive(Debug, Clone, Default)] +pub(crate) struct Duplex { + pub reader: io::Cursor>, + pub writer: io::Cursor>, +} + +#[derive(Debug, Clone, Default)] +pub(crate) struct MockStream { + pub du: Arc>, +} + +impl MockStream { + pub fn push_bytes_to_read(&mut self, bytes: &[u8]) { + self.du.lock().unwrap().reader.get_mut().extend(bytes); + } + + pub fn pop_bytes_written(&mut self) -> Vec { + let mut du = self.du.lock().unwrap(); + let mut wr = Vec::new(); + mem::swap(&mut wr, du.writer.get_mut()); + du.writer.set_position(0); + wr + } +} + +pub(crate) struct Inner { + pub take_next: usize, + pub streams: Vec, +} + +impl Inner { + pub fn take_stream(&mut self) -> Option { + self.take_next += 1; + self.streams.get(self.take_next - 1).cloned() + } +} diff --git a/tests/mock/mod.rs b/tests/mock/mod.rs index 86835b21..6a4707b4 100644 --- a/tests/mock/mod.rs +++ b/tests/mock/mod.rs @@ -1,25 +1,17 @@ use faktory::Reconnect; -use mockstream::SyncMockStream; -use std::io; -use std::sync::{Arc, Mutex}; +use std::{ + io, + pin::Pin, + sync::{Arc, Mutex}, +}; +use tokio::io::{AsyncRead, AsyncWrite}; -struct Inner { - take_next: usize, - streams: Vec, -} - -impl Inner { - fn take_stream(&mut self) -> Option { - self.take_next += 1; - - self.streams.get(self.take_next - 1).cloned() - } -} +mod inner; #[derive(Clone)] pub struct Stream { - mine: Option, - all: Arc>, + mine: inner::MockStream, + all: Arc>, } impl Default for Stream { @@ -28,8 +20,9 @@ impl Default for Stream { } } +#[async_trait::async_trait] impl Reconnect for Stream { - fn reconnect(&self) -> io::Result { + async fn reconnect(&mut self) -> Result { let mine = self .all .lock() @@ -37,25 +30,47 @@ impl Reconnect for Stream { .take_stream() .expect("tried to make a new stream, but no more connections expected"); Ok(Stream { - mine: Some(mine), + mine, all: Arc::clone(&self.all), }) } } -impl io::Read for Stream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.mine.as_mut().unwrap().read(buf) +impl AsyncRead for Stream { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + let mut duplex = self.mine.du.lock().unwrap(); + Pin::new(&mut duplex.reader).poll_read(cx, buf) } } -impl io::Write for Stream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.mine.as_mut().unwrap().write(buf) +impl AsyncWrite for Stream { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let mut duplex = self.mine.du.lock().unwrap(); + Pin::new(&mut duplex.writer).poll_write(cx, buf) + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let mut duplex = self.mine.du.lock().unwrap(); + Pin::new(&mut duplex.writer).poll_flush(cx) } - fn flush(&mut self) -> io::Result<()> { - self.mine.as_mut().unwrap().flush() + fn poll_shutdown( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let mut duplex = self.mine.du.lock().unwrap(); + Pin::new(&mut duplex.writer).poll_shutdown(cx) } } @@ -63,7 +78,8 @@ impl Stream { fn make(salt: Option<(usize, &str)>, streams: usize) -> Self { let streams = (0..streams) .map(|_| { - let mut s = SyncMockStream::new(); + let mut s = inner::MockStream::default(); + eprintln!("{:#?}", s); // need to say HELLO if let Some((iters, salt)) = salt { // include salt for pwdhash @@ -79,11 +95,11 @@ impl Stream { }) .collect(); - let mut inner = Inner { + let mut inner = inner::Inner { take_next: 0, streams, }; - let mine = inner.take_stream(); + let mine = inner.take_stream().unwrap(); Stream { mine, diff --git a/tests/producer.rs b/tests/producer.rs index 1db4c82d..d283da1b 100644 --- a/tests/producer.rs +++ b/tests/producer.rs @@ -1,5 +1,4 @@ extern crate faktory; -extern crate mockstream; extern crate serde_json; extern crate url; @@ -7,12 +6,13 @@ mod mock; use faktory::*; -#[test] -fn hello() { +#[tokio::test(flavor = "multi_thread")] +async fn hello() { let mut s = mock::Stream::default(); - let p = Producer::connect_with(s.clone(), None).unwrap(); + let p = Producer::connect_with(s.clone(), None).await.unwrap(); let written = s.pop_bytes_written(0); + eprintln!("{:?}", String::from_utf8(written.clone()).unwrap()); assert!(written.starts_with(b"HELLO {")); let written: serde_json::Value = serde_json::from_slice(&written[b"HELLO ".len()..]).unwrap(); let written = written.as_object().unwrap(); @@ -27,11 +27,13 @@ fn hello() { assert_eq!(written, b"END\r\n"); } -#[test] -fn hello_pwd() { +#[tokio::test(flavor = "multi_thread")] +async fn hello_pwd() { let mut s = mock::Stream::with_salt(1545, "55104dc76695721d"); - let c = Producer::connect_with(s.clone(), Some("foobar".to_string())).unwrap(); + let c = Producer::connect_with(s.clone(), Some("foobar".to_string())) + .await + .unwrap(); let written = s.pop_bytes_written(0); assert!(written.starts_with(b"HELLO {")); let written: serde_json::Value = serde_json::from_slice(&written[b"HELLO ".len()..]).unwrap(); @@ -44,14 +46,14 @@ fn hello_pwd() { drop(c); } -#[test] -fn enqueue() { +#[tokio::test(flavor = "multi_thread")] +async fn enqueue() { let mut s = mock::Stream::default(); - let mut p = Producer::connect_with(s.clone(), None).unwrap(); + let mut p = Producer::connect_with(s.clone(), None).await.unwrap(); s.ignore(0); s.ok(0); - p.enqueue(Job::new("foobar", vec!["z"])).unwrap(); + p.enqueue(Job::new("foobar", vec!["z"])).await.unwrap(); let written = s.pop_bytes_written(0); assert!(written.starts_with(b"PUSH {")); @@ -85,17 +87,18 @@ fn enqueue() { assert_eq!(written.get("backtrace").and_then(|h| h.as_u64()), Some(0)); } -#[test] -fn queue_control() { +#[tokio::test(flavor = "multi_thread")] +async fn queue_control() { let mut s = mock::Stream::default(); - let mut p = Producer::connect_with(s.clone(), None).unwrap(); + let mut p = Producer::connect_with(s.clone(), None).await.unwrap(); s.ignore(0); s.ok(0); - p.queue_pause(&["test", "test2"]).unwrap(); + p.queue_pause(&["test", "test2"]).await.unwrap(); s.ok(0); p.queue_resume(&["test3".to_string(), "test4".to_string()]) + .await .unwrap(); let written = s.pop_bytes_written(0); diff --git a/tests/real/community.rs b/tests/real/community.rs index 8b45840a..13b9ce21 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -1,106 +1,104 @@ extern crate faktory; -extern crate serde_json; -extern crate url; -use faktory::*; +use crate::skip_check; +use faktory::{ConsumerBuilder, Job, JobBuilder, Producer}; use serde_json::Value; -use std::io; -use std::sync; - -macro_rules! skip_check { - () => { - if std::env::var_os("FAKTORY_URL").is_none() { - return; - } - }; -} +use std::{io, sync}; -#[test] -fn hello_p() { +#[tokio::test(flavor = "multi_thread")] +async fn hello_p() { skip_check!(); - let p = Producer::connect(None).unwrap(); + let p = Producer::connect(None).await.unwrap(); drop(p); } -#[test] -fn hello_c() { +#[tokio::test(flavor = "multi_thread")] +async fn enqueue_job() { skip_check!(); - let mut c = ConsumerBuilder::default(); - c.hostname("tester".to_string()) - .wid("hello".to_string()) - .labels(vec!["foo".to_string(), "bar".to_string()]); - c.register("never_called", |_| -> io::Result<()> { unreachable!() }); - let c = c.connect(None).unwrap(); - drop(c); + let mut p = Producer::connect(None).await.unwrap(); + p.enqueue(JobBuilder::new("order").build()).await.unwrap(); } -#[test] -fn roundtrip() { - skip_check!(); - let local = "roundtrip"; +async fn process_order(j: Job) -> Result<(), std::io::Error> { + println!("{:?}", j); + assert_eq!(j.kind(), "order"); + Ok(()) +} - let (tx, rx) = sync::mpsc::channel(); - let tx = sync::Arc::new(sync::Mutex::new(tx)); +#[tokio::test(flavor = "multi_thread")] +async fn roundtrip() { + skip_check!(); + let jid = String::from("x-job-id-0123456782"); let mut c = ConsumerBuilder::default(); - c.hostname("tester".to_string()).wid(local.to_string()); - { - let tx = sync::Arc::clone(&tx); - c.register(local, move |j| -> io::Result<()> { - tx.lock().unwrap().send(j).unwrap(); + c.register("order", |job| Box::pin(process_order(job))); + c.register("image", |job| { + Box::pin(async move { + println!("{:?}", job); + assert_eq!(job.kind(), "image"); Ok(()) - }); - } - let mut c = c.connect(None).unwrap(); - - let mut p = Producer::connect(None).unwrap(); - p.enqueue(Job::new(local, vec!["z"]).on_queue(local)) - .unwrap(); - c.run_one(0, &[local]).unwrap(); - let job = rx.recv().unwrap(); - assert_eq!(job.queue, local); - assert_eq!(job.kind(), local); - assert_eq!(job.args(), &[Value::from("z")]); + }) + }); + let mut c = c.connect(None).await.unwrap(); + let mut p = Producer::connect(None).await.unwrap(); + p.enqueue( + JobBuilder::new("order") + .jid(&jid) + .args(vec!["ISBN-13:9781718501850"]) + .queue("roundtrip") + .build(), + ) + .await + .unwrap(); + let had_one = c.run_one(0, &["roundtrip"]).await.unwrap(); + assert!(had_one); + + let drained = !c.run_one(0, &["roundtrip"]).await.unwrap(); + assert!(drained); } -#[test] -fn multi() { +#[tokio::test(flavor = "multi_thread")] +async fn multi() { skip_check!(); - let local = "multi"; + let local = "multi_async"; let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); - let mut c = ConsumerBuilder::default(); + let mut c = ConsumerBuilder::default_async(); c.hostname("tester".to_string()).wid(local.to_string()); - { + + c.register(local, move |j| { let tx = sync::Arc::clone(&tx); - c.register(local, move |j| -> io::Result<()> { + Box::pin(async move { tx.lock().unwrap().send(j).unwrap(); - Ok(()) - }); - } - let mut c = c.connect(None).unwrap(); + Ok::<(), io::Error>(()) + }) + }); - let mut p = Producer::connect(None).unwrap(); + let mut c = c.connect(None).await.unwrap(); + + let mut p = Producer::connect(None).await.unwrap(); p.enqueue(Job::new(local, vec![Value::from(1), Value::from("foo")]).on_queue(local)) + .await .unwrap(); p.enqueue(Job::new(local, vec![Value::from(2), Value::from("bar")]).on_queue(local)) + .await .unwrap(); - c.run_one(0, &[local]).unwrap(); + c.run_one(0, &[local]).await.unwrap(); let job = rx.recv().unwrap(); assert_eq!(job.queue, local); assert_eq!(job.kind(), local); assert_eq!(job.args(), &[Value::from(1), Value::from("foo")]); - c.run_one(0, &[local]).unwrap(); + c.run_one(0, &[local]).await.unwrap(); let job = rx.recv().unwrap(); assert_eq!(job.queue, local); assert_eq!(job.kind(), local); assert_eq!(job.args(), &[Value::from(2), Value::from("bar")]); } -#[test] -fn fail() { +#[tokio::test(flavor = "multi_thread")] +async fn fail() { skip_check!(); let local = "fail"; @@ -108,33 +106,35 @@ fn fail() { let tx = sync::Arc::new(sync::Mutex::new(tx)); let mut c = ConsumerBuilder::default(); c.hostname("tester".to_string()).wid(local.to_string()); - { + + c.register(local, move |j| { let tx = sync::Arc::clone(&tx); - c.register(local, move |j| -> io::Result<()> { + Box::pin(async move { tx.lock().unwrap().send(j).unwrap(); Err(io::Error::new(io::ErrorKind::Other, "nope")) - }); - } - let mut c = c.connect(None).unwrap(); + }) + }); + + let mut c = c.connect(None).await.unwrap(); - let mut p = Producer::connect(None).unwrap(); + let mut p = Producer::connect(None).await.unwrap(); // note that *enqueueing* the jobs didn't fail! p.enqueue(Job::new(local, vec![Value::from(1), Value::from("foo")]).on_queue(local)) + .await .unwrap(); p.enqueue(Job::new(local, vec![Value::from(2), Value::from("bar")]).on_queue(local)) + .await .unwrap(); - c.run_one(0, &[local]).unwrap(); - c.run_one(0, &[local]).unwrap(); + c.run_one(0, &[local]).await.unwrap(); + c.run_one(0, &[local]).await.unwrap(); drop(c); assert_eq!(rx.into_iter().take(2).count(), 2); - - // TODO: check that jobs *actually* failed! } -#[test] -fn queue() { +#[tokio::test(flavor = "multi_thread")] +async fn queue() { skip_check!(); let local = "pause"; @@ -143,29 +143,33 @@ fn queue() { let mut c = ConsumerBuilder::default(); c.hostname("tester".to_string()).wid(local.to_string()); - c.register(local, move |_job| tx.lock().unwrap().send(true)); - let mut c = c.connect(None).unwrap(); + c.register(local, move |_job| { + let tx = sync::Arc::clone(&tx); + Box::pin(async move { tx.lock().unwrap().send(true) }) + }); + let mut c = c.connect(None).await.unwrap(); - let mut p = Producer::connect(None).unwrap(); + let mut p = Producer::connect(None).await.unwrap(); p.enqueue(Job::new(local, vec![Value::from(1)]).on_queue(local)) + .await .unwrap(); - p.queue_pause(&[local]).unwrap(); + p.queue_pause(&[local]).await.unwrap(); - let had_job = c.run_one(0, &[local]).unwrap(); + let had_job = c.run_one(0, &[local]).await.unwrap(); assert!(!had_job); let worker_executed = rx.try_recv().is_ok(); assert!(!worker_executed); - p.queue_resume(&[local]).unwrap(); + p.queue_resume(&[local]).await.unwrap(); - let had_job = c.run_one(0, &[local]).unwrap(); + let had_job = c.run_one(0, &[local]).await.unwrap(); assert!(had_job); let worker_executed = rx.try_recv().is_ok(); assert!(worker_executed); } -#[test] -fn test_jobs_pushed_in_bulk() { +#[tokio::test(flavor = "multi_thread")] +async fn test_jobs_pushed_in_bulk() { skip_check!(); let local_1 = "test_jobs_pushed_in_bulk_1"; @@ -173,13 +177,14 @@ fn test_jobs_pushed_in_bulk() { let local_3 = "test_jobs_pushed_in_bulk_3"; let local_4 = "test_jobs_pushed_in_bulk_4"; - let mut p = Producer::connect(None).unwrap(); + let mut p = Producer::connect(None).await.unwrap(); let (enqueued_count, errors) = p .enqueue_many(vec![ Job::builder("common").queue(local_1).build(), Job::builder("common").queue(local_2).build(), Job::builder("special").queue(local_2).build(), ]) + .await .unwrap(); assert_eq!(enqueued_count, 3); assert!(errors.is_none()); // error-free @@ -207,6 +212,7 @@ fn test_jobs_pushed_in_bulk() { Job::builder("very_special").queue(local_4).build(), Job::builder("very_special").queue(local_4).build(), ]) + .await .unwrap(); // 3 out of 5 not enqueued; @@ -231,37 +237,45 @@ fn test_jobs_pushed_in_bulk() { // is not an all-or-nothing operation: let mut c = ConsumerBuilder::default(); c.hostname("tester".to_string()).wid(local_3.to_string()); - c.register("very_special", move |_job| -> io::Result<()> { Ok(()) }); - c.register("broken", move |_job| -> io::Result<()> { Ok(()) }); - let mut c = c.connect(None).unwrap(); + c.register("very_special", move |_job| { + Box::pin(async move { Ok::<(), io::Error>(()) }) + }); + c.register("broken", move |_job| { + Box::pin(async move { Ok::<(), io::Error>(()) }) + }); + let mut c = c.connect(None).await.unwrap(); // we targeted "very_special" jobs to "local_4" queue - assert!(c.run_one(0, &[local_4]).unwrap()); - assert!(c.run_one(0, &[local_4]).unwrap()); - assert!(!c.run_one(0, &[local_4]).unwrap()); // drained + assert!(c.run_one(0, &[local_4]).await.unwrap()); + assert!(c.run_one(0, &[local_4]).await.unwrap()); + assert!(!c.run_one(0, &[local_4]).await.unwrap()); // drained // also let's check that the 'broken' jobs have NOT been enqueued, // reminder: we target the broken jobs to "local_3" queue - assert!(!c.run_one(0, &[local_3]).unwrap()); // empty + assert!(!c.run_one(0, &[local_3]).await.unwrap()); // empty +} + +async fn assert_args_empty(j: Job) -> io::Result<()> { + assert!(j.args().is_empty()); + Ok(eprintln!("{:?}", j)) } -#[test] -fn test_jobs_created_with_builder() { +async fn assert_args_not_empty(j: Job) -> io::Result<()> { + assert!(j.args().len() != 0); + Ok(eprintln!("{:?}", j)) +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_jobs_created_with_builder() { skip_check!(); // prepare a producer ("client" in Faktory terms) and consumer ("worker"): - let mut producer = Producer::connect(None).unwrap(); + let mut producer = Producer::connect(None).await.unwrap(); let mut consumer = ConsumerBuilder::default(); - consumer.register("rebuild_index", move |job| -> io::Result<_> { - assert!(job.args().is_empty()); - Ok(eprintln!("{:?}", job)) - }); - consumer.register("register_order", move |job| -> io::Result<_> { - assert!(job.args().len() != 0); - Ok(eprintln!("{:?}", job)) - }); + consumer.register("rebuild_index", |j| Box::pin(assert_args_empty(j))); + consumer.register("register_order", |j| Box::pin(assert_args_not_empty(j))); - let mut consumer = consumer.connect(None).unwrap(); + let mut consumer = consumer.connect(None).await.unwrap(); // prepare some jobs with JobBuilder: let job1 = JobBuilder::new("rebuild_index") @@ -277,23 +291,26 @@ fn test_jobs_created_with_builder() { job3.queue = "test_jobs_created_with_builder_1".to_string(); // enqueue ... - producer.enqueue(job1).unwrap(); - producer.enqueue(job2).unwrap(); - producer.enqueue(job3).unwrap(); + producer.enqueue(job1).await.unwrap(); + producer.enqueue(job2).await.unwrap(); + producer.enqueue(job3).await.unwrap(); // ... and execute: let had_job = consumer .run_one(0, &["test_jobs_created_with_builder_0"]) + .await .unwrap(); assert!(had_job); let had_job = consumer .run_one(0, &["test_jobs_created_with_builder_1"]) + .await .unwrap(); assert!(had_job); let had_job = consumer .run_one(0, &["test_jobs_created_with_builder_1"]) + .await .unwrap(); assert!(had_job); } diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index e2fd807b..f3620657 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -1,42 +1,31 @@ extern crate faktory; -extern crate serde_json; -extern crate url; +use crate::skip_if_not_enterprise; +use crate::utils::learn_faktory_url; use chrono::Utc; use faktory::ent::*; use faktory::*; use serde_json::Value; use std::io; +use tokio::time; -macro_rules! skip_if_not_enterprise { - () => { - if std::env::var_os("FAKTORY_ENT").is_none() { - return; - } - }; +async fn print_job(j: Job) -> io::Result<()> { + Ok(eprintln!("{:?}", j)) } - macro_rules! assert_had_one { ($c:expr, $q:expr) => { - let had_one_job = $c.run_one(0, &[$q]).unwrap(); + let had_one_job = $c.run_one(0, &[$q]).await.unwrap(); assert!(had_one_job); }; } macro_rules! assert_is_empty { ($c:expr, $q:expr) => { - let had_one_job = $c.run_one(0, &[$q]).unwrap(); + let had_one_job = $c.run_one(0, &[$q]).await.unwrap(); assert!(!had_one_job); }; } -fn learn_faktory_url() -> String { - let url = std::env::var_os("FAKTORY_URL").expect( - "Enterprise Faktory should be running for this test, and 'FAKTORY_URL' environment variable should be provided", - ); - url.to_str().expect("Is a utf-8 string").to_owned() -} - fn some_jobs(kind: S, q: S, count: usize) -> impl Iterator where S: Into + Clone + 'static, @@ -46,21 +35,18 @@ where .map(move |_| Job::builder(kind.clone()).queue(q.clone()).build()) } -#[test] -fn ent_expiring_job() { - use std::{thread, time}; - +#[tokio::test(flavor = "multi_thread")] +async fn ent_expiring_job() { skip_if_not_enterprise!(); let url = learn_faktory_url(); + let local = "ent_expiring_job"; // prepare a producer ("client" in Faktory terms) and consumer ("worker"): - let mut p = Producer::connect(Some(&url)).unwrap(); + let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register("AnExpiringJob", move |job| -> io::Result<_> { - Ok(eprintln!("{:?}", job)) - }); - let mut c = c.connect(Some(&url)).unwrap(); + c.register("AnExpiringJob", |j| Box::pin(print_job(j))); + let mut c = c.connect(Some(&url)).await.unwrap(); // prepare an expiring job: let job_ttl_secs: u64 = 3; @@ -68,12 +54,12 @@ fn ent_expiring_job() { let ttl = chrono::Duration::seconds(job_ttl_secs as i64); let job1 = JobBuilder::new("AnExpiringJob") .args(vec!["ISBN-13:9781718501850"]) - .queue("ent_expiring_job") + .queue(local) .expires_at(chrono::Utc::now() + ttl) .build(); // enqueue and fetch immediately job1: - p.enqueue(job1).unwrap(); + p.enqueue(job1).await.unwrap(); assert_had_one!(&mut c, "ent_expiring_job"); // check that the queue is drained: @@ -82,21 +68,20 @@ fn ent_expiring_job() { // prepare another one: let job2 = JobBuilder::new("AnExpiringJob") .args(vec!["ISBN-13:9781718501850"]) - .queue("ent_expiring_job") + .queue(local) .expires_at(chrono::Utc::now() + ttl) .build(); // enqueue and then fetch job2, but after ttl: - p.enqueue(job2).unwrap(); - thread::sleep(time::Duration::from_secs(job_ttl_secs * 2)); - + p.enqueue(job2).await.unwrap(); + tokio::time::sleep(time::Duration::from_secs(job_ttl_secs * 2)).await; // For the non-enterprise edition of Faktory, this assertion will // fail, which should be taken into account when running the test suite on CI. - assert_is_empty!(&mut c, "ent_expiring_job"); + assert_is_empty!(&mut c, local); } -#[test] -fn ent_unique_job() { +#[tokio::test(flavor = "multi_thread")] +async fn ent_unique_job() { use faktory::error; use serde_json::Value; @@ -107,12 +92,10 @@ fn ent_unique_job() { let job_type = "order"; // prepare producer and consumer: - let mut p = Producer::connect(Some(&url)).unwrap(); + let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register(job_type, |job| -> io::Result<_> { - Ok(eprintln!("{:?}", job)) - }); - let mut c = c.connect(Some(&url)).unwrap(); + c.register(job_type, |j| Box::pin(print_job(j))); + let mut c = c.connect(Some(&url)).await.unwrap(); // Reminder. Jobs are considered unique for kind + args + queue. // So the following two jobs, will be accepted by Faktory, since we @@ -123,18 +106,20 @@ fn ent_unique_job() { .args(args.clone()) .queue(queue_name) .build(); - p.enqueue(job1).unwrap(); + + p.enqueue(job1).await.unwrap(); let job2 = JobBuilder::new(job_type) .args(args.clone()) .queue(queue_name) .build(); - p.enqueue(job2).unwrap(); - let had_job = c.run_one(0, &[queue_name]).unwrap(); + p.enqueue(job2).await.unwrap(); + + let had_job = c.run_one(0, &[queue_name]).await.unwrap(); assert!(had_job); - let had_another_one = c.run_one(0, &[queue_name]).unwrap(); + let had_another_one = c.run_one(0, &[queue_name]).await.unwrap(); assert!(had_another_one); - let and_that_is_it_for_now = !c.run_one(0, &[queue_name]).unwrap(); + let and_that_is_it_for_now = !c.run_one(0, &[queue_name]).await.unwrap(); assert!(and_that_is_it_for_now); // let's now create a unique job and followed by a job with @@ -146,7 +131,9 @@ fn ent_unique_job() { .queue(queue_name) .unique_for(unique_for_secs) .build(); - p.enqueue(job1).unwrap(); + + p.enqueue(job1).await.unwrap(); + // this one is a 'duplicate' ... let job2 = Job::builder(job_type) .args(args.clone()) @@ -154,7 +141,8 @@ fn ent_unique_job() { .unique_for(unique_for_secs) .build(); // ... so the server will respond accordingly: - let res = p.enqueue(job2).unwrap_err(); + let res = p.enqueue(job2).await.unwrap_err(); + if let error::Error::Protocol(error::Protocol::UniqueConstraintViolation { msg }) = res { assert_eq!(msg, "Job not unique"); } else { @@ -162,12 +150,14 @@ fn ent_unique_job() { } // Let's now consume the job which is 'holding' a unique lock: - let had_job = c.run_one(0, &[queue_name]).unwrap(); + let had_job = c.run_one(0, &[queue_name]).await.unwrap(); + assert!(had_job); // And check that the queue is really empty (`job2` from above // has not been queued indeed): - let queue_is_empty = !c.run_one(0, &[queue_name]).unwrap(); + let queue_is_empty = !c.run_one(0, &[queue_name]).await.unwrap(); + assert!(queue_is_empty); // Now let's repeat the latter case, but providing different args to job2: @@ -176,7 +166,9 @@ fn ent_unique_job() { .queue(queue_name) .unique_for(unique_for_secs) .build(); - p.enqueue(job1).unwrap(); + + p.enqueue(job1).await.unwrap(); + // this one is *NOT* a 'duplicate' ... let job2 = JobBuilder::new(job_type) .args(vec![Value::from("ISBN-13:9781718501850"), Value::from(101)]) @@ -184,7 +176,13 @@ fn ent_unique_job() { .unique_for(unique_for_secs) .build(); // ... so the server will accept it: - p.enqueue(job2).unwrap(); + + p.enqueue(job2).await.unwrap(); + + let had_job = c.run_one(0, &[queue_name]).await.unwrap(); + assert!(had_job); + let had_another_one = c.run_one(0, &[queue_name]).await.unwrap(); + assert!(had_another_one); assert_had_one!(&mut c, queue_name); assert_had_one!(&mut c, queue_name); @@ -192,11 +190,11 @@ fn ent_unique_job() { assert_is_empty!(&mut c, queue_name); } -#[test] -fn ent_unique_job_until_success() { +#[tokio::test(flavor = "multi_thread")] +async fn ent_unique_job_until_success() { use faktory::error; - use std::thread; - use std::time; + use std::io; + use tokio::time; skip_if_not_enterprise!(); @@ -211,42 +209,45 @@ fn ent_unique_job_until_success() { let unique_for = 4; let url1 = url.clone(); - let handle = thread::spawn(move || { + let handle = tokio::spawn(async move { // prepare producer and consumer, where the former can // send a job difficulty level as a job's args and the lattter // will sleep for a corresponding period of time, pretending // to work hard: - let mut producer_a = Producer::connect(Some(&url1)).unwrap(); - let mut consumer_a = ConsumerBuilder::default(); - consumer_a.register(job_type, |job| -> io::Result<_> { - let args = job.args().to_owned(); - let mut args = args.iter(); - let diffuculty_level = args - .next() - .expect("job difficulty level is there") - .to_owned(); - let sleep_secs = - serde_json::from_value::(diffuculty_level).expect("a valid number"); - thread::sleep(time::Duration::from_secs(sleep_secs as u64)); - Ok(eprintln!("{:?}", job)) + let mut producer_a = Producer::connect(Some(&url1)).await.unwrap(); + let mut consumer_a = ConsumerBuilder::default_async(); + consumer_a.register(job_type, |job| { + Box::pin(async move { + let args = job.args().to_owned(); + let mut args = args.iter(); + let diffuculty_level = args + .next() + .expect("job difficulty level is there") + .to_owned(); + let sleep_secs = + serde_json::from_value::(diffuculty_level).expect("a valid number"); + time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; + eprintln!("{:?}", job); + Ok::<(), io::Error>(()) + }) }); - let mut consumer_a = consumer_a.connect(Some(&url1)).unwrap(); + let mut consumer_a = consumer_a.connect(Some(&url1)).await.unwrap(); let job = JobBuilder::new(job_type) .args(vec![difficulty_level]) .queue(queue_name) .unique_for(unique_for) .unique_until_success() // Faktory's default .build(); - producer_a.enqueue(job).unwrap(); - let had_job = consumer_a.run_one(0, &[queue_name]).unwrap(); + producer_a.enqueue(job).await.unwrap(); + let had_job = consumer_a.run_one(0, &[queue_name]).await.unwrap(); assert!(had_job); }); // let spawned thread gain momentum: - thread::sleep(time::Duration::from_secs(1)); + time::sleep(time::Duration::from_secs(1)).await; // continue - let mut producer_b = Producer::connect(Some(&url)).unwrap(); + let mut producer_b = Producer::connect(Some(&url)).await.unwrap(); // this one is a 'duplicate' because the job is still // being executed in the spawned thread: @@ -257,14 +258,14 @@ fn ent_unique_job_until_success() { .build(); // as a result: - let res = producer_b.enqueue(job).unwrap_err(); + let res = producer_b.enqueue(job).await.unwrap_err(); if let error::Error::Protocol(error::Protocol::UniqueConstraintViolation { msg }) = res { assert_eq!(msg, "Job not unique"); } else { panic!("Expected protocol error.") } - handle.join().expect("should join successfully"); + handle.await.expect("should join successfully"); // Now that the job submitted in a spawned thread has been successfully executed // (with ACK sent to server), the producer 'B' can push another one: @@ -276,13 +277,13 @@ fn ent_unique_job_until_success() { .unique_for(unique_for) .build(), ) + .await .unwrap(); } -#[test] -fn ent_unique_job_until_start() { - use std::thread; - use std::time; +#[tokio::test(flavor = "multi_thread")] +async fn ent_unique_job_until_start() { + use tokio::time; skip_if_not_enterprise!(); @@ -294,22 +295,25 @@ fn ent_unique_job_until_start() { let unique_for = 4; let url1 = url.clone(); - let handle = thread::spawn(move || { - let mut producer_a = Producer::connect(Some(&url1)).unwrap(); - let mut consumer_a = ConsumerBuilder::default(); - consumer_a.register(job_type, |job| -> io::Result<_> { - let args = job.args().to_owned(); - let mut args = args.iter(); - let diffuculty_level = args - .next() - .expect("job difficulty level is there") - .to_owned(); - let sleep_secs = - serde_json::from_value::(diffuculty_level).expect("a valid number"); - thread::sleep(time::Duration::from_secs(sleep_secs as u64)); - Ok(eprintln!("{:?}", job)) + let handle = tokio::spawn(async move { + let mut producer_a = Producer::connect(Some(&url1)).await.unwrap(); + let mut consumer_a = ConsumerBuilder::default_async(); + consumer_a.register(job_type, |job| { + Box::pin(async move { + let args = job.args().to_owned(); + let mut args = args.iter(); + let diffuculty_level = args + .next() + .expect("job difficulty level is there") + .to_owned(); + let sleep_secs = + serde_json::from_value::(diffuculty_level).expect("a valid number"); + time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; + eprintln!("{:?}", job); + Ok::<(), io::Error>(()) + }) }); - let mut consumer_a = consumer_a.connect(Some(&url1)).unwrap(); + let mut consumer_a = consumer_a.connect(Some(&url1)).await.unwrap(); producer_a .enqueue( JobBuilder::new(job_type) @@ -319,17 +323,18 @@ fn ent_unique_job_until_start() { .unique_until_start() // NB! .build(), ) + .await .unwrap(); // as soon as the job is fetched, the unique lock gets released - let had_job = consumer_a.run_one(0, &[queue_name]).unwrap(); + let had_job = consumer_a.run_one(0, &[queue_name]).await.unwrap(); assert!(had_job); }); // let spawned thread gain momentum: - thread::sleep(time::Duration::from_secs(1)); + time::sleep(time::Duration::from_secs(1)).await; // the unique lock has been released by this time, so the job is enqueued successfully: - let mut producer_b = Producer::connect(Some(&url)).unwrap(); + let mut producer_b = Producer::connect(Some(&url)).await.unwrap(); producer_b .enqueue( JobBuilder::new(job_type) @@ -338,20 +343,20 @@ fn ent_unique_job_until_start() { .unique_for(unique_for) .build(), ) + .await .unwrap(); - handle.join().expect("should join successfully"); + handle.await.expect("should join successfully"); } -#[test] -fn ent_unique_job_bypass_unique_lock() { +#[tokio::test(flavor = "multi_thread")] +async fn ent_unique_job_bypass_unique_lock() { use faktory::error; skip_if_not_enterprise!(); let url = learn_faktory_url(); - - let mut producer = Producer::connect(Some(&url)).unwrap(); + let mut producer = Producer::connect(Some(&url)).await.unwrap(); let queue_name = "ent_unique_job_bypass_unique_lock"; let job1 = Job::builder("order") .queue(queue_name) @@ -365,8 +370,8 @@ fn ent_unique_job_bypass_unique_lock() { .queue(queue_name) // same queue .build(); // NB: `unique_for` not set - producer.enqueue(job1).unwrap(); - producer.enqueue(job2).unwrap(); // bypassing the lock! + producer.enqueue(job1).await.unwrap(); + producer.enqueue(job2).await.unwrap(); // bypassing the lock! // This _is_ a 'duplicate'. let job3 = Job::builder("order") @@ -374,7 +379,7 @@ fn ent_unique_job_bypass_unique_lock() { .unique_for(60) // NB .build(); - let res = producer.enqueue(job3).unwrap_err(); // NOT bypassing the lock! + let res = producer.enqueue(job3).await.unwrap_err(); // NOT bypassing the lock! if let error::Error::Protocol(error::Protocol::UniqueConstraintViolation { msg }) = res { assert_eq!(msg, "Job not unique"); @@ -384,19 +389,18 @@ fn ent_unique_job_bypass_unique_lock() { // let's consume three times from the queue to verify that the first two jobs // have been enqueued for real, while the last one has not. - let mut c = ConsumerBuilder::default(); - c.register("order", |j| -> io::Result<_> { Ok(eprintln!("{:?}", j)) }); - let mut c = c.connect(Some(&url)).unwrap(); + let mut c = ConsumerBuilder::default_async(); + c.register("order", |j| Box::pin(print_job(j))); + let mut c = c.connect(Some(&url)).await.unwrap(); - assert!(c.run_one(0, &[queue_name]).unwrap()); - assert!(c.run_one(0, &[queue_name]).unwrap()); - assert!(!c.run_one(0, &[queue_name]).unwrap()); // empty; + assert!(c.run_one(0, &[queue_name]).await.unwrap()); + assert!(c.run_one(0, &[queue_name]).await.unwrap()); + assert!(!c.run_one(0, &[queue_name]).await.unwrap()); // empty; } -#[test] -fn test_tracker_can_send_and_retrieve_job_execution_progress() { +#[tokio::test(flavor = "multi_thread")] +async fn test_tracker_can_send_and_retrieve_job_execution_progress() { use std::{ - io, sync::{Arc, Mutex}, thread, time, }; @@ -406,12 +410,12 @@ fn test_tracker_can_send_and_retrieve_job_execution_progress() { let url = learn_faktory_url(); let t = Arc::new(Mutex::new( - Client::connect(Some(&url)).expect("job progress tracker created successfully"), + Client::connect(Some(&url)) + .await + .expect("job progress tracker created successfully"), )); - let t_captured = Arc::clone(&t); - - let mut p = Producer::connect(Some(&url)).unwrap(); + let mut p = Producer::connect(Some(&url)).await.unwrap(); let job_tackable = JobBuilder::new("order") .args(vec![Value::from("ISBN-13:9781718501850")]) @@ -427,56 +431,65 @@ fn test_tracker_can_send_and_retrieve_job_execution_progress() { // let's remember this job's id: let job_id = job_tackable.id().to_owned(); - let job_id_captured = job_id.clone(); - p.enqueue(job_tackable).expect("enqueued"); + p.enqueue(job_tackable).await.expect("enqueued"); let mut c = ConsumerBuilder::default(); - c.register("order", move |job| -> io::Result<_> { - // trying to set progress on a community edition of Faktory will give: - // 'an internal server error occurred: tracking subsystem is only available in Faktory Enterprise' - assert!(t_captured - .lock() - .expect("lock acquired") - .set_progress( - ProgressUpdate::builder(&job_id_captured) - .desc("Still processing...".to_owned()) - .percent(32) - .build(), - ) - .is_ok()); - // Let's update the progress once again, to check the 'set_progress' shortcut: - assert!(t_captured - .lock() - .unwrap() - .set_progress(ProgressUpdate::set(&job_id_captured, 33)) - .is_ok()); - - // let's sleep for a while ... - thread::sleep(time::Duration::from_secs(2)); - - // ... and read the progress info - let result = t_captured - .lock() - .expect("lock acquired") - .get_progress(job_id_captured.clone()) - .expect("Retrieved progress update over the wire"); - - assert!(result.is_some()); - let result = result.unwrap(); - assert_eq!(result.jid, job_id_captured.clone()); - match result.state { - JobState::Working => {} - _ => panic!("expected job's state to be 'working'"), - } - assert!(result.updated_at.is_some()); - assert_eq!(result.percent, Some(33)); - // considering the job done - Ok(eprintln!("{:?}", job)) - }); + { + let job_id = job_id.clone(); + let url = url.clone(); + c.register("order", move |job| { + let job_id = job_id.clone(); + let url = url.clone(); + Box::pin(async move { + let mut t = Client::connect(Some(&url)) + .await + .expect("job progress tracker created successfully"); + + // trying to set progress on a community edition of Faktory will give: + // 'an internal server error occurred: tracking subsystem is only available in Faktory Enterprise' + assert!(t + .set_progress( + ProgressUpdate::builder(&job_id.clone()) + .desc("Still processing...".to_owned()) + .percent(32) + .build(), + ) + .await + .is_ok()); + // Let's update the progress once again, to check the 'set_progress' shortcut: + assert!(t + .set_progress(ProgressUpdate::set(&job_id.clone(), 33)) + .await + .is_ok()); + + // let's sleep for a while ... + thread::sleep(time::Duration::from_secs(2)); + + // ... and read the progress info + let result = t + .get_progress(job_id.clone()) + .await + .expect("Retrieved progress update over the wire"); + + assert!(result.is_some()); + let result = result.unwrap(); + assert_eq!(result.jid, job_id.clone()); + match result.state { + JobState::Working => {} + _ => panic!("expected job's state to be 'working'"), + } + assert!(result.updated_at.is_some()); + assert_eq!(result.percent, Some(33)); + // considering the job done + Ok::<(), io::Error>(eprintln!("{:?}", job)) + }) + }); + } let mut c = c .connect(Some(&url)) + .await .expect("Successfully ran a handshake with 'Faktory'"); assert_had_one!(&mut c, "test_tracker_can_send_progress_update"); @@ -484,6 +497,7 @@ fn test_tracker_can_send_and_retrieve_job_execution_progress() { .lock() .expect("lock acquired successfully") .get_progress(job_id.clone()) + .await .expect("Retrieved progress update over the wire once again") .expect("Some progress"); @@ -502,19 +516,20 @@ fn test_tracker_can_send_and_retrieve_job_execution_progress() { .desc("Final stage.".to_string()) .percent(99) .build(); - assert!(t.lock().unwrap().set_progress(upd).is_ok()); + assert!(t.lock().unwrap().set_progress(upd).await.is_ok()); let progress = t .lock() .unwrap() .get_progress(job_id) + .await .expect("Retrieved progress update over the wire once again") .expect("Some progress"); if progress.percent != Some(100) { let upd = progress.update_percent(100); assert_eq!(upd.desc, progress.desc); - assert!(t.lock().unwrap().set_progress(upd).is_ok()) + assert!(t.lock().unwrap().set_progress(upd).await.is_ok()) } // What about 'ordinary' job ? @@ -522,6 +537,7 @@ fn test_tracker_can_send_and_retrieve_job_execution_progress() { // Sending it ... p.enqueue(job_ordinary) + .await .expect("Successfuly send to Faktory"); // ... and asking for its progress @@ -529,6 +545,7 @@ fn test_tracker_can_send_and_retrieve_job_execution_progress() { .lock() .expect("lock acquired") .get_progress(job_id.clone()) + .await .expect("Retrieved progress update over the wire once again") .expect("Some progress"); @@ -546,17 +563,21 @@ fn test_tracker_can_send_and_retrieve_job_execution_progress() { assert!(progress.desc.is_none()); } -#[test] -fn test_batch_of_jobs_can_be_initiated() { +#[tokio::test(flavor = "multi_thread")] +async fn test_batch_of_jobs_can_be_initiated() { skip_if_not_enterprise!(); let url = learn_faktory_url(); - let mut p = Producer::connect(Some(&url)).unwrap(); + let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register("thumbnail", move |_job| -> io::Result<_> { Ok(()) }); - c.register("clean_up", move |_job| -> io::Result<_> { Ok(()) }); - let mut c = c.connect(Some(&url)).unwrap(); - let mut t = Client::connect(Some(&url)).expect("job progress tracker created successfully"); + c.register("thumbnail", move |_job| { + Box::pin(async move { Ok::<(), io::Error>(()) }) + }); + c.register("clean_up", move |_job| Box::pin(async move { Ok(()) })); + let mut c = c.connect(Some(&url)).await.unwrap(); + let mut t = Client::connect(Some(&url)) + .await + .expect("job progress tracker created successfully"); let job_1 = Job::builder("thumbnail") .args(vec!["path/to/original/image1"]) @@ -582,21 +603,22 @@ fn test_batch_of_jobs_can_be_initiated() { let time_just_before_batch_init = Utc::now(); - let mut b = p.start_batch(batch).unwrap(); + let mut b = p.start_batch(batch).await.unwrap(); // let's remember batch id: let bid = b.id().to_string(); - assert!(b.add(job_1).unwrap().is_none()); - assert!(b.add(job_2).unwrap().is_none()); - assert_eq!(b.add(job_3).unwrap().unwrap(), "check-check"); - b.commit().unwrap(); + assert!(b.add(job_1).await.unwrap().is_none()); + assert!(b.add(job_2).await.unwrap().is_none()); + assert_eq!(b.add(job_3).await.unwrap().unwrap(), "check-check"); + b.commit().await.unwrap(); // The batch has been committed, let's see its status: let time_just_before_getting_status = Utc::now(); let s = t .get_batch_status(bid.clone()) + .await .expect("successfully fetched batch status from server...") .expect("...and it's not none"); @@ -622,6 +644,7 @@ fn test_batch_of_jobs_can_be_initiated() { // we have consumed one job from this batch: let s = t .get_batch_status(bid.clone()) + .await .expect("successfully fetched batch status from server...") .expect("...and it's not none"); @@ -638,6 +661,7 @@ fn test_batch_of_jobs_can_be_initiated() { // let's check batch status once again: let s = t .get_batch_status(bid.clone()) + .await .expect("successfully fetched batch status from server...") .expect("...and it's not none"); @@ -653,6 +677,7 @@ fn test_batch_of_jobs_can_be_initiated() { // all the jobs from the batch have been executed: let s = t .get_batch_status(bid.clone()) + .await .expect("successfully fetched batch status from server...") .expect("...and it's not none"); @@ -668,6 +693,7 @@ fn test_batch_of_jobs_can_be_initiated() { // let's check batch status one last time: let s = t .get_batch_status(bid.clone()) + .await .expect("successfully fetched batch status from server...") .expect("...and it's not none"); @@ -675,17 +701,21 @@ fn test_batch_of_jobs_can_be_initiated() { assert_eq!(s.complete_callback_state, CallbackState::FinishedOk); } -#[test] -fn test_batches_can_be_nested() { +#[tokio::test(flavor = "multi_thread")] +async fn test_batches_can_be_nested() { skip_if_not_enterprise!(); let url = learn_faktory_url(); // Set up 'producer', 'consumer', and 'tracker': - let mut p = Producer::connect(Some(&url)).unwrap(); + let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register("jobtype", move |_job| -> io::Result<_> { Ok(()) }); - let mut _c = c.connect(Some(&url)).unwrap(); - let mut t = Client::connect(Some(&url)).expect("job progress tracker created successfully"); + c.register("jobtype", move |_job| { + Box::pin(async move { Ok::<(), io::Error>(()) }) + }); + let mut _c = c.connect(Some(&url)).await.unwrap(); + let mut t = Client::connect(Some(&url)) + .await + .expect("job progress tracker created successfully"); // Prepare some jobs: let parent_job1 = Job::builder("jobtype") @@ -718,44 +748,53 @@ fn test_batches_can_be_nested() { let parent_batch = Batch::builder() .description("Parent batch") .with_success_callback(parent_cb_job); - let mut parent_batch = p.start_batch(parent_batch).unwrap(); + let mut parent_batch = p.start_batch(parent_batch).await.unwrap(); let parent_batch_id = parent_batch.id().to_owned(); - parent_batch.add(parent_job1).unwrap(); + parent_batch.add(parent_job1).await.unwrap(); let child_batch = Batch::builder() .description("Child batch") .with_success_callback(child_cb_job); - let mut child_batch = parent_batch.start_batch(child_batch).unwrap(); + let mut child_batch = parent_batch.start_batch(child_batch).await.unwrap(); let child_batch_id = child_batch.id().to_owned(); - child_batch.add(child_job_1).unwrap(); - child_batch.add(child_job_2).unwrap(); + child_batch.add(child_job_1).await.unwrap(); + child_batch.add(child_job_2).await.unwrap(); let grandchild_batch = Batch::builder() .description("Grandchild batch") .with_success_callback(grandchild_cb_job); - let mut grandchild_batch = child_batch.start_batch(grandchild_batch).unwrap(); + let mut grandchild_batch = child_batch.start_batch(grandchild_batch).await.unwrap(); let grandchild_batch_id = grandchild_batch.id().to_owned(); - grandchild_batch.add(grand_child_job_1).unwrap(); + grandchild_batch.add(grand_child_job_1).await.unwrap(); - grandchild_batch.commit().unwrap(); - child_batch.commit().unwrap(); - parent_batch.commit().unwrap(); + grandchild_batch.commit().await.unwrap(); + child_batch.commit().await.unwrap(); + parent_batch.commit().await.unwrap(); // batches finish let parent_status = t .get_batch_status(parent_batch_id.clone()) + .await .unwrap() .unwrap(); assert_eq!(parent_status.description, Some("Parent batch".to_string())); assert_eq!(parent_status.total, 1); assert_eq!(parent_status.parent_bid, None); - let child_status = t.get_batch_status(child_batch_id.clone()).unwrap().unwrap(); + let child_status = t + .get_batch_status(child_batch_id.clone()) + .await + .unwrap() + .unwrap(); assert_eq!(child_status.description, Some("Child batch".to_string())); assert_eq!(child_status.total, 2); assert_eq!(child_status.parent_bid, Some(parent_batch_id)); - let grandchild_status = t.get_batch_status(grandchild_batch_id).unwrap().unwrap(); + let grandchild_status = t + .get_batch_status(grandchild_batch_id) + .await + .unwrap() + .unwrap(); assert_eq!( grandchild_status.description, Some("Grandchild batch".to_string()) @@ -764,18 +803,20 @@ fn test_batches_can_be_nested() { assert_eq!(grandchild_status.parent_bid, Some(child_batch_id)); } -#[test] -fn test_callback_will_not_be_queued_unless_batch_gets_committed() { +#[tokio::test(flavor = "multi_thread")] +async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { skip_if_not_enterprise!(); let url = learn_faktory_url(); // prepare a producer, a consumer of 'order' jobs, and a tracker: - let mut p = Producer::connect(Some(&url)).unwrap(); + let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register("order", move |_job| -> io::Result<_> { Ok(()) }); - c.register("order_clean_up", move |_job| -> io::Result<_> { Ok(()) }); - let mut c = c.connect(Some(&url)).unwrap(); - let mut t = Client::connect(Some(&url)).unwrap(); + c.register("order", move |_job| Box::pin(async move { Ok(()) })); + c.register("order_clean_up", move |_job| { + Box::pin(async move { Ok::<(), io::Error>(()) }) + }); + let mut c = c.connect(Some(&url)).await.unwrap(); + let mut t = Client::connect(Some(&url)).await.unwrap(); let mut jobs = some_jobs( "order", @@ -795,16 +836,17 @@ fn test_callback_will_not_be_queued_unless_batch_gets_committed() { .description("Orders processing workload") .with_success_callback(callbacks.next().unwrap()), ) + .await .unwrap(); let bid = b.id().to_string(); // push 3 jobs onto this batch, but DO NOT commit the batch: for _ in 0..3 { - b.add(jobs.next().unwrap()).unwrap(); + b.add(jobs.next().unwrap()).await.unwrap(); } // check this batch's status: - let s = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.total, 3); assert_eq!(s.pending, 3); assert_eq!(s.success_callback_state, CallbackState::Pending); @@ -824,7 +866,7 @@ fn test_callback_will_not_be_queued_unless_batch_gets_committed() { ); // check this batch's status again: - let s = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.total, 3); assert_eq!(s.pending, 0); assert_eq!(s.failed, 0); @@ -837,10 +879,10 @@ fn test_callback_will_not_be_queued_unless_batch_gets_committed() { ); // now let's COMMIT the batch ... - b.commit().unwrap(); + b.commit().await.unwrap(); // ... and check batch status: - let s = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.success_callback_state, CallbackState::Enqueued); // finally, let's consume from the success callbacks queue ... @@ -850,18 +892,18 @@ fn test_callback_will_not_be_queued_unless_batch_gets_committed() { ); // ... and see the final status: - let s = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.success_callback_state, CallbackState::FinishedOk); } -#[test] -fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { +#[tokio::test(flavor = "multi_thread")] +async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { use std::{thread, time}; skip_if_not_enterprise!(); let url = learn_faktory_url(); - let mut p = Producer::connect(Some(&url)).unwrap(); - let mut t = Client::connect(Some(&url)).unwrap(); + let mut p = Producer::connect(Some(&url)).await.unwrap(); + let mut t = Client::connect(Some(&url)).await.unwrap(); let q_name = "test_callback_will_be_queued_upon_commit_even_if_batch_is_empty"; let complete_cb_jobtype = "complete_callback_jobtype"; let success_cb_jobtype = "success_cb_jobtype"; @@ -873,20 +915,21 @@ fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { .description("Orders processing workload") .with_callbacks(success_cb, complete_cb), ) + .await .unwrap(); let bid = b.id().to_owned(); - let s = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.total, 0); // no jobs in the batch; assert_eq!(s.success_callback_state, CallbackState::Pending); assert_eq!(s.complete_callback_state, CallbackState::Pending); - b.commit().unwrap(); + b.commit().await.unwrap(); // let's give the Faktory server some time: thread::sleep(time::Duration::from_secs(2)); - let s = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.total, 0); // again, there are no jobs in the batch ... // The docs say "If you don't push any jobs into the batch, any callbacks will fire immediately upon BATCH COMMIT." @@ -895,19 +938,21 @@ fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { assert_eq!(s.success_callback_state, CallbackState::Pending); let mut c = ConsumerBuilder::default(); - c.register(complete_cb_jobtype, move |_job| -> io::Result<_> { Ok(()) }); - c.register(success_cb_jobtype, move |_job| -> io::Result<_> { - Err(io::Error::new( - io::ErrorKind::Other, - "we want this one to fail to test the 'CallbackState' behavior", - )) + c.register(complete_cb_jobtype, |_job| Box::pin(async { Ok(()) })); + c.register(success_cb_jobtype, |_job| { + Box::pin(async { + Err(io::Error::new( + io::ErrorKind::Other, + "we want this one to fail to test the 'CallbackState' behavior", + )) + }) }); - let mut c = c.connect(Some(&url)).unwrap(); + let mut c = c.connect(Some(&url)).await.unwrap(); assert_had_one!(&mut c, q_name); // complete callback consumed - let s = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.total, 0); match s.complete_callback_state { CallbackState::FinishedOk => {} @@ -919,7 +964,7 @@ fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { } assert_had_one!(&mut c, q_name); // success callback consumed - let s = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.total, 0); assert_eq!(s.complete_callback_state, CallbackState::FinishedOk); // Still `Enqueued` due to the fact that it was not finished with success. @@ -928,12 +973,12 @@ fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { assert_eq!(s.success_callback_state, CallbackState::Enqueued); } -#[test] -fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { +#[tokio::test(flavor = "multi_thread")] +async fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { skip_if_not_enterprise!(); let url = learn_faktory_url(); - let mut p = Producer::connect(Some(&url)).unwrap(); - let mut t = Client::connect(Some(&url)).unwrap(); + let mut p = Producer::connect(Some(&url)).await.unwrap(); + let mut t = Client::connect(Some(&url)).await.unwrap(); let mut jobs = some_jobs("order", "test_batch_can_be_reopned_add_extra_jobs_added", 4); let mut callbacks = some_jobs( "order_clean_up", @@ -945,18 +990,21 @@ fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { .description("Orders processing workload") .with_success_callback(callbacks.next().unwrap()); - let mut b = p.start_batch(b).unwrap(); + let mut b = p.start_batch(b).await.unwrap(); let bid = b.id().to_string(); - b.add(jobs.next().unwrap()).unwrap(); // 1 job - b.add(jobs.next().unwrap()).unwrap(); // 2 jobs + b.add(jobs.next().unwrap()).await.unwrap(); // 1 job + b.add(jobs.next().unwrap()).await.unwrap(); // 2 jobs - let status = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let status = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(status.total, 2); assert_eq!(status.pending, 2); // ############################## SUBTEST 0 ########################################## // Let's try to open/reopen a batch we have never declared: - let b = p.open_batch(String::from("non-existent-batch-id")).unwrap(); + let b = p + .open_batch(String::from("non-existent-batch-id")) + .await + .unwrap(); // The server will error back on this, with "No such batch ", but // we are handling this case for the end-user and returning `Ok(None)` instead, indicating // this way that there is not such batch. @@ -967,12 +1015,12 @@ fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { // Let's fist of all try to open the batch we have not committed yet: // [We can use `producer::open_batch` specifying a bid OR - even we previously retrived // a status for this batch, we can go with `status::open` providing an exclusive ref to producer] - let mut b = status.open(&mut p).unwrap().unwrap(); - b.add(jobs.next().unwrap()).unwrap(); // 3 jobs + let mut b = status.open(&mut p).await.unwrap().unwrap(); + b.add(jobs.next().unwrap()).await.unwrap(); // 3 jobs - b.commit().unwrap(); // committig the batch + b.commit().await.unwrap(); // committig the batch - let status = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let status = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(status.total, 3); assert_eq!(status.pending, 3); @@ -991,12 +1039,13 @@ fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { // Let's try to open an already committed batch: let mut b = p .open_batch(bid.clone()) + .await .expect("no error") .expect("is some"); - b.add(jobs.next().unwrap()).unwrap(); // 4 jobs - b.commit().unwrap(); // committing the batch again! + b.add(jobs.next().unwrap()).await.unwrap(); // 4 jobs + b.commit().await.unwrap(); // committing the batch again! - let s = t.get_batch_status(bid.clone()).unwrap().unwrap(); + let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.total, 4); assert_eq!(s.pending, 4); @@ -1007,7 +1056,7 @@ fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { // ############################## SUBTEST 3 ############################################ // Let's see if we will be able to - again - open the committed batch "from outside" and // add a nested batch to it. - let mut b = p.open_batch(bid.clone()).unwrap().expect("is some"); + let mut b = p.open_batch(bid.clone()).await.unwrap().expect("is some"); let mut nested_callbacks = some_jobs( "order_clean_up__NESTED", "test_batch_can_be_reopned_add_extra_jobs_added__CALLBACKs__NESTED", @@ -1019,13 +1068,17 @@ fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { nested_callbacks.next().unwrap(), nested_callbacks.next().unwrap(), ); - let nested_batch = b.start_batch(nested_batch_declaration).unwrap(); + let nested_batch = b.start_batch(nested_batch_declaration).await.unwrap(); let nested_bid = nested_batch.id().to_string(); // committing the nested batch without any jobs // since those are just not relevant for this test: - nested_batch.commit().unwrap(); + nested_batch.commit().await.unwrap(); - let s = t.get_batch_status(nested_bid.clone()).unwrap().unwrap(); + let s = t + .get_batch_status(nested_bid.clone()) + .await + .unwrap() + .unwrap(); assert_eq!(s.total, 0); assert_eq!(s.parent_bid, Some(bid)); // this is really our child batch assert_eq!(s.complete_callback_state, CallbackState::Enqueued); @@ -1048,6 +1101,7 @@ fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { // Let's try to re-open the nested batch that we have already committed and add some jobs to it. let mut b = p .open_batch(nested_bid.clone()) + .await .expect("no error") .expect("is some"); let mut more_jobs = some_jobs( @@ -1055,11 +1109,15 @@ fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { "test_batch_can_be_reopned_add_extra_jobs_added__NESTED", 2, ); - b.add(more_jobs.next().unwrap()).unwrap(); - b.add(more_jobs.next().unwrap()).unwrap(); - b.commit().unwrap(); + b.add(more_jobs.next().unwrap()).await.unwrap(); + b.add(more_jobs.next().unwrap()).await.unwrap(); + b.commit().await.unwrap(); - let s = t.get_batch_status(nested_bid.clone()).unwrap().unwrap(); + let s = t + .get_batch_status(nested_bid.clone()) + .await + .unwrap() + .unwrap(); match s.complete_callback_state { CallbackState::Enqueued => {} _ => panic!("Expected the callback to have been enqueued"), diff --git a/tests/real/main.rs b/tests/real/main.rs index b8b8f3dd..7fbfb954 100644 --- a/tests/real/main.rs +++ b/tests/real/main.rs @@ -1,4 +1,5 @@ mod community; +mod utils; #[cfg(feature = "ent")] mod enterprise; diff --git a/tests/real/utils.rs b/tests/real/utils.rs new file mode 100644 index 00000000..9fefb92f --- /dev/null +++ b/tests/real/utils.rs @@ -0,0 +1,25 @@ +#[macro_export] +macro_rules! skip_check { + () => { + if std::env::var_os("FAKTORY_URL").is_none() { + return; + } + }; +} + +#[macro_export] +macro_rules! skip_if_not_enterprise { + () => { + if std::env::var_os("FAKTORY_ENT").is_none() { + return; + } + }; +} + +#[cfg(feature = "ent")] +pub fn learn_faktory_url() -> String { + let url = std::env::var_os("FAKTORY_URL").expect( + "Enterprise Faktory should be running for this test, and 'FAKTORY_URL' environment variable should be provided", + ); + url.to_str().expect("Is a utf-8 string").to_owned() +} diff --git a/tests/tls.rs b/tests/tls.rs index 3260ea7c..2811209b 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -4,9 +4,9 @@ use faktory::*; use serde_json::Value; use std::{env, fs, io, sync}; -#[test] -fn roundtrip_tls() { - use native_tls::{Certificate, TlsConnector}; +#[tokio::test(flavor = "multi_thread")] +async fn roundtrip_tls() { + use tokio_rustls::rustls::{ClientConfig, RootCertStore}; // We are utilizing the fact that the "FAKTORY_URL_SECURE" environment variable is set // as an indicator that the integration test can and should be performed. @@ -28,10 +28,12 @@ fn roundtrip_tls() { let mut c = ConsumerBuilder::default(); c.hostname("tester".to_string()).wid(local.to_string()); { - let tx = sync::Arc::clone(&tx); - c.register(local, move |j| -> io::Result<()> { - tx.lock().unwrap().send(j).unwrap(); - Ok(()) + c.register(local, move |j| { + let tx = sync::Arc::clone(&tx); + Box::pin(async move { + tx.lock().unwrap().send(j).unwrap(); + Ok::<(), io::Error>(()) + }) }); } @@ -40,32 +42,39 @@ fn roundtrip_tls() { .join("docker") .join("certs") .join("faktory.local.crt"); - let cert = fs::read_to_string(cert_path).unwrap(); + let cert = fs::read(cert_path).unwrap(); - let tls = || { - let connector = if cfg!(target_os = "macos") { - TlsConnector::builder() - // Danger! Only for testing! - // On the macos CI runner, the certs are not trusted: - // { code: -67843, message: "The certificate was not trusted." } - .danger_accept_invalid_certs(true) - .build() - .unwrap() - } else { - let cert = Certificate::from_pem(cert.as_bytes()).unwrap(); - TlsConnector::builder() - .add_root_certificate(cert) - .build() - .unwrap() - }; - TlsStream::with_connector(connector, Some(&env::var("FAKTORY_URL_SECURE").unwrap())) + let tls = || async { + // let connector = if cfg!(target_os = "macos") { + // TlsConnector::builder() + // // Danger! Only for testing! + // // On the macos CI runner, the certs are not trusted: + // // { code: -67843, message: "The certificate was not trusted." } + // .danger_accept_invalid_certs(true) + // .build() + // .unwrap() + // } else { + // let cert = Certificate::from_pem(cert.as_bytes()).unwrap(); + // TlsConnector::builder() + // .add_root_certificate(cert) + // .build() + // .unwrap() + // }; + let mut store = RootCertStore::empty(); + store.add(cert.clone().into()).unwrap(); + let conf = ClientConfig::builder() + .with_root_certificates(store) + .with_no_client_auth(); + TlsStream::with_client_config(conf, Some(&env::var("FAKTORY_URL_SECURE").unwrap())) + .await .unwrap() }; - let mut c = c.connect_with(tls(), None).unwrap(); - let mut p = Producer::connect_with(tls(), None).unwrap(); + let mut c = c.connect_with(tls().await, None).await.unwrap(); + let mut p = Producer::connect_with(tls().await, None).await.unwrap(); p.enqueue(Job::new(local, vec!["z"]).on_queue(local)) + .await .unwrap(); - c.run_one(0, &[local]).unwrap(); + c.run_one(0, &[local]).await.unwrap(); let job = rx.recv().unwrap(); assert_eq!(job.queue, local); From cbeb2ce2a8a9be45c28e42e88b08c8bbbcddb8fb Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sat, 24 Feb 2024 17:16:53 +0500 Subject: [PATCH 002/129] Use x509-parser for tls test. Add test verifier --- .gitignore | 2 +- Cargo.lock | 218 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + tests/real/enterprise.rs | 6 -- tests/tls.rs | 124 ++++++++++++++++------ 5 files changed, 312 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index 67b3c5c6..0f735146 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /target/ **/*.rs.bk perf.* -.vscode \ No newline at end of file +.vscode diff --git a/Cargo.lock b/Cargo.lock index d1488183..e6f1ab2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,6 +80,45 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -273,6 +312,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_builder" version = "0.12.0" @@ -314,6 +382,17 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + [[package]] name = "faktory" version = "0.12.5" @@ -336,6 +415,7 @@ dependencies = [ "tokio-rustls", "tokio-test", "url", + "x509-parser", ] [[package]] @@ -457,6 +537,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.153" @@ -481,6 +567,12 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -501,6 +593,42 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.18" @@ -529,6 +657,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -567,6 +704,12 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -642,6 +785,15 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustls" version = "0.22.2" @@ -777,6 +929,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + [[package]] name = "thiserror" version = "1.0.57" @@ -797,6 +961,37 @@ dependencies = [ "syn 2.0.50", ] +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -902,6 +1097,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "untrusted" version = "0.9.0" @@ -1154,6 +1355,23 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "x509-parser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 3929cee4..ad404475 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ url = "2" [dev-dependencies] tokio = { version = "1.35.1", features = ["rt", "macros"] } tokio-test = "0.4.3" +x509-parser = "0.15.1" [[bin]] name = "loadtest" diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index f3620657..fa2f6b54 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -176,14 +176,8 @@ async fn ent_unique_job() { .unique_for(unique_for_secs) .build(); // ... so the server will accept it: - p.enqueue(job2).await.unwrap(); - let had_job = c.run_one(0, &[queue_name]).await.unwrap(); - assert!(had_job); - let had_another_one = c.run_one(0, &[queue_name]).await.unwrap(); - assert!(had_another_one); - assert_had_one!(&mut c, queue_name); assert_had_one!(&mut c, queue_name); // and the queue is empty again: diff --git a/tests/tls.rs b/tests/tls.rs index 2811209b..e851a0cd 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -2,12 +2,15 @@ use faktory::*; use serde_json::Value; -use std::{env, fs, io, sync}; +use std::{ + env, io, + sync::{self, Arc}, +}; +use tokio_rustls::rustls::ClientConfig; +use tokio_rustls::rustls::SignatureScheme; #[tokio::test(flavor = "multi_thread")] async fn roundtrip_tls() { - use tokio_rustls::rustls::{ClientConfig, RootCertStore}; - // We are utilizing the fact that the "FAKTORY_URL_SECURE" environment variable is set // as an indicator that the integration test can and should be performed. // @@ -37,38 +40,28 @@ async fn roundtrip_tls() { }); } - let cert_path = env::current_dir() - .unwrap() - .join("docker") - .join("certs") - .join("faktory.local.crt"); - let cert = fs::read(cert_path).unwrap(); - let tls = || async { - // let connector = if cfg!(target_os = "macos") { - // TlsConnector::builder() - // // Danger! Only for testing! - // // On the macos CI runner, the certs are not trusted: - // // { code: -67843, message: "The certificate was not trusted." } - // .danger_accept_invalid_certs(true) - // .build() - // .unwrap() - // } else { - // let cert = Certificate::from_pem(cert.as_bytes()).unwrap(); - // TlsConnector::builder() - // .add_root_certificate(cert) - // .build() - // .unwrap() - // }; - let mut store = RootCertStore::empty(); - store.add(cert.clone().into()).unwrap(); - let conf = ClientConfig::builder() - .with_root_certificates(store) + let verifier = fixtures::TestServerCertVerifier::new( + SignatureScheme::RSA_PSS_SHA512, + env::current_dir() + .unwrap() + .join("docker") + .join("certs") + .join("faktory.local.crt"), + ); + let client_config = ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(Arc::new(verifier)) .with_no_client_auth(); - TlsStream::with_client_config(conf, Some(&env::var("FAKTORY_URL_SECURE").unwrap())) - .await - .unwrap() + + TlsStream::with_client_config( + client_config, + Some(&env::var("FAKTORY_URL_SECURE").unwrap()), + ) + .await + .unwrap() }; + let mut c = c.connect_with(tls().await, None).await.unwrap(); let mut p = Producer::connect_with(tls().await, None).await.unwrap(); p.enqueue(Job::new(local, vec!["z"]).on_queue(local)) @@ -81,3 +74,70 @@ async fn roundtrip_tls() { assert_eq!(job.kind(), local); assert_eq!(job.args(), &[Value::from("z")]); } + +mod fixtures { + #![allow(unused_variables)] + + use std::fs; + use std::path::PathBuf; + + use tokio_rustls::rustls::client::danger::{ + HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, + }; + use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime}; + use tokio_rustls::rustls::DigitallySignedStruct; + use tokio_rustls::rustls::Error as RustlsError; + use tokio_rustls::rustls::SignatureScheme; + use x509_parser::pem::parse_x509_pem; + + #[derive(Debug)] + pub(super) struct TestServerCertVerifier<'a> { + scheme: SignatureScheme, + cert_der: CertificateDer<'a>, + } + + impl TestServerCertVerifier<'_> { + pub(super) fn new(scheme: SignatureScheme, cert_path: PathBuf) -> Self { + let cert = fs::read(&cert_path).unwrap(); + let (_, pem) = parse_x509_pem(&cert).unwrap(); + let cert_der = CertificateDer::try_from(pem.contents).unwrap(); + Self { scheme, cert_der } + } + } + + impl ServerCertVerifier for TestServerCertVerifier<'_> { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + assert_eq!(&self.cert_der, end_entity); + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![self.scheme] + } + } +} From a3b99c423f508f2159445d237a94c9da664fd769 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sat, 24 Feb 2024 18:12:49 +0500 Subject: [PATCH 003/129] Make minimal versions pass again --- Cargo.lock | 911 +++++++++++++++++++++++------------------------------ Cargo.toml | 6 + 2 files changed, 396 insertions(+), 521 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6f1ab2b..e576899b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,39 +4,24 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ "gimli", ] [[package]] name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] +checksum = "6aa100a6f6f525226719f8de3f70076be4f4191801ebd92621450d1c51e9053d" [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "bff2cf94a3dbe2d57cbd56485e1bd7436455058034d6c2d47be51d4e5e4bc6ab" dependencies = [ "anstyle", "anstyle-parse", @@ -48,63 +33,63 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "asn1-rs" -version = "0.5.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +checksum = "e577111f9ca51289da894bcb4b17047737218c2e4477ea2fc36cd3922172062f" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", "displaydoc", "nom", - "num-traits", + "num-traits 0.2.14", "rusticata-macros", "thiserror", - "time", + "time 0.3.7", ] [[package]] name = "asn1-rs-derive" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +checksum = "6b9002415e8baa0177a3ae0946fb62ca6e9e470755717409134e44d8e0ae2cad" dependencies = [ "proc-macro2", - "quote", - "syn 1.0.109", + "quote 1.0.35", + "syn 1.0.91", "synstructure", ] @@ -115,30 +100,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", - "quote", - "syn 1.0.109", + "quote 1.0.35", + "syn 1.0.91", ] [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.50", + "quote 1.0.35", + "syn 1.0.91", ] [[package]] @@ -148,25 +132,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.50", + "quote 1.0.35", + "syn 2.0.46", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "88fb5a785d6b44fd9d6700935608639af1b8356de1e55d5f7c2740f4faa15d82" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", @@ -175,30 +159,30 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" dependencies = [ "generic-array", ] [[package]] -name = "bumpalo" -version = "3.15.1" +name = "bytes" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c764d619ca78fccbf3069b37bd7af92577f044bb15236036662d79b6559f25b7" +checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72" [[package]] -name = "bytes" -version = "1.5.0" +name = "cc" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" [[package]] -name = "cc" -version = "1.0.86" +name = "cfg-if" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" +checksum = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" [[package]] name = "cfg-if" @@ -208,43 +192,42 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", + "num-integer", + "num-traits 0.2.14", "serde", - "windows-targets 0.52.0", + "time 0.1.36", ] [[package]] name = "clap" -version = "4.5.1" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "colorchoice" @@ -252,36 +235,29 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" dependencies = [ "generic-array", - "typenum", ] [[package]] name = "darling" -version = "0.14.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "f1a5d2e8b5a94b2261efb20e99a01255b9c5293797d69bbf04600567b2f9b8d7" dependencies = [ "darling_core", "darling_macro", @@ -289,58 +265,49 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "8f1c7d56716be82d9c6adb967cfe700955179ea88806e898483dad6987330a54" dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "quote 1.0.35", + "strsim", + "syn 1.0.91", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "64dd7e5a75a00cb6799ae9fbbfc3bba0134def6579a9e27564e72c839c837bed" dependencies = [ "darling_core", - "quote", - "syn 1.0.109", + "quote 1.0.35", + "syn 1.0.91", ] [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" [[package]] name = "der-parser" -version = "8.2.0" +version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" dependencies = [ "asn1-rs", "displaydoc", "nom", "num-bigint", - "num-traits", + "num-traits 0.2.14", "rusticata-macros", ] -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - [[package]] name = "derive_builder" version = "0.12.0" @@ -358,8 +325,8 @@ checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling", "proc-macro2", - "quote", - "syn 1.0.109", + "quote 1.0.35", + "syn 1.0.91", ] [[package]] @@ -369,14 +336,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ "derive_builder_core", - "syn 1.0.109", + "syn 1.0.91", ] [[package]] name = "digest" -version = "0.10.7" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" dependencies = [ "block-buffer", "crypto-common", @@ -384,15 +351,21 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "278ef1934318d524612205f69df005eea30ec10edf7913e500b5a527fce55bc0" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.50", + "quote 1.0.35", + "syn 1.0.91", ] +[[package]] +name = "dtoa" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" + [[package]] name = "faktory" version = "0.12.5" @@ -404,8 +377,11 @@ dependencies = [ "fnv", "hostname", "libc", + "num-bigint", + "oid-registry", "pin-project", "rand", + "rustls", "serde", "serde_derive", "serde_json", @@ -424,26 +400,17 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "30f0ab78f035d7ed5d52689f4b05a56c15ad80097f1d860e644bdc9dba3831f2" [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", "version_check", @@ -451,59 +418,30 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi", ] [[package]] name = "gimli" -version = "0.28.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "hermit-abi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" [[package]] name = "hostname" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "01b1af8d6d068ba9de1c39c6ff0d879aed20f74873d4d3929a4535000bb07886" dependencies = [ "libc", "match_cfg", - "winapi", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", + "winapi 0.3.0", ] [[package]] @@ -514,27 +452,35 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ + "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "itoa" -version = "1.0.10" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "91fd9dc2c587067de817fec4ad355e3818c3d893a78cab32a0a474c7a15bb8d5" [[package]] -name = "js-sys" -version = "0.3.68" +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "kernel32-sys" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "ad1ca084b49bfd975182288e1a5f1d27ea34ff2d6ae084ae5e66e1652427eada" dependencies = [ - "wasm-bindgen", + "winapi 0.2.4", + "winapi-build", ] [[package]] @@ -545,15 +491,18 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "log" -version = "0.4.20" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "cba860f648db8e6f269df990180c2217f333472b4a6e901e97446858487971e2" +dependencies = [ + "cfg-if 0.1.2", +] [[package]] name = "match_cfg" @@ -561,102 +510,118 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +[[package]] +name = "matches" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e" + [[package]] name = "memchr" -version = "2.7.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "e01e64d9017d18e7fc09d8e4fe0e28ff6931019e979fb8019319db7ca827f8a6" +dependencies = [ + "libc", +] [[package]] name = "minimal-lexical" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.10" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "nom" -version = "7.1.3" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" dependencies = [ "memchr", "minimal-lexical", + "version_check", ] [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" dependencies = [ "autocfg", "num-integer", - "num-traits", + "num-traits 0.2.14", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "num-traits", + "autocfg", + "num-traits 0.2.14", ] [[package]] name = "num-traits" -version = "0.2.18" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "51eab148f171aefad295f8cece636fc488b9b392ef544da31ea4b8ef6b9e9c39" + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" dependencies = [ - "hermit-abi", "libc", ] [[package]] -name = "object" -version = "0.32.2" +name = "num_threads" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52" dependencies = [ - "memchr", + "libc", ] +[[package]] +name = "object" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" + [[package]] name = "oid-registry" version = "0.6.1" @@ -666,17 +631,11 @@ dependencies = [ "asn1-rs", ] -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "ba4f28a6faf4ffea762ba8f4baef48c61a6db348647c73095034041fc79dd954" [[package]] name = "pin-project" @@ -694,37 +653,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.50", + "quote 1.0.35", + "syn 2.0.46", ] [[package]] name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "powerfmt" -version = "0.2.0" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" dependencies = [ "unicode-ident", ] +[[package]] +name = "quote" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0fc799e40f2a2c2be239825b30b686f1bd1d2e0e3d5e943b14c1380db49acf" + [[package]] name = "quote" version = "1.0.35" @@ -736,20 +695,21 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "a76330fb486679b4ace3670f117bbc9e16204005c4bde9c4bd372f45bed34f12" dependencies = [ "libc", "rand_chacha", "rand_core", + "rand_hc", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", "rand_core", @@ -757,48 +717,62 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a48131ab10dbeb17202bd1dcb9c9798963a58a50c9ec31640f237358832094" + [[package]] name = "ring" -version = "0.17.8" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "fb9d44f9bf6b635117787f72416783eb7e4227aaf255e5ce739563d817176a7e" dependencies = [ "cc", - "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" [[package]] name = "rusticata-macros" -version = "4.1.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +checksum = "65c52377bb2288aa522a0c8208947fada1e0c76397f108cc08f57efe6077b50d" dependencies = [ "nom", ] [[package]] name = "rustls" -version = "0.22.2" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48" dependencies = [ "log", "ring", @@ -810,15 +784,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.3.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" +checksum = "eb0a1f9b9efec70d32e6d6aa3e58ebd88c3754ec98dfe9145c63cf54cc829b83" [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89" dependencies = [ "ring", "rustls-pki-types", @@ -826,49 +800,51 @@ dependencies = [ ] [[package]] -name = "ryu" -version = "1.0.17" +name = "serde" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "369633cfe0f0bde1dfc037fb6c5a329d46586a31f981bed14d87487a3439ae37" [[package]] -name = "serde" -version = "1.0.197" +name = "serde_derive" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "6a61ecb8511aaff381424f98b49a059017420ec60e15e8d63b645701af7fa9b8" dependencies = [ - "serde_derive", + "quote 0.3.8", + "serde_derive_internals", + "syn 0.11.10", ] [[package]] -name = "serde_derive" -version = "1.0.197" +name = "serde_derive_internals" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "021c338d22c7e30f957a6ab7e388cb6098499dda9fd4ba1661ee074ca7a180d1" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.50", + "syn 0.11.10", + "synom", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "e9b1ec939469a124b27e208106550c38358ed4334d2b1b5b3825bc1ee37d946a" dependencies = [ - "itoa", - "ryu", + "dtoa", + "itoa 0.3.0", + "num-traits 0.1.32", "serde", ] [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] @@ -880,14 +856,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "spin" -version = "0.9.8" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" [[package]] name = "strsim" @@ -895,12 +871,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strsim" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" - [[package]] name = "subtle" version = "2.5.0" @@ -909,109 +879,113 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "1.0.109" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "171b739972d9a1bfb169e8077238b51f9ebeaae4ff6e08072f7ba386a8802da2" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "quote 0.3.8", + "synom", + "unicode-xid 0.0.4", ] [[package]] name = "syn" -version = "2.0.50" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" dependencies = [ "proc-macro2", - "quote", - "unicode-ident", + "quote 1.0.35", + "unicode-xid 0.2.0", ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "syn" +version = "2.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" dependencies = [ "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", + "quote 1.0.35", + "unicode-ident", ] [[package]] -name = "thiserror" -version = "1.0.57" +name = "synom" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "8fece1853fb872b0acdc3ff88f37c474018e125ef81cd4cb8c0ca515746b62ed" dependencies = [ - "thiserror-impl", + "unicode-xid 0.0.4", ] [[package]] -name = "thiserror-impl" -version = "1.0.57" +name = "synstructure" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "affc27d5f1764f7487bafeb41e380664790716e38ba45d8487bddcc53e79f0f6" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.50", + "quote 1.0.35", + "syn 1.0.91", + "unicode-xid 0.1.0", ] [[package]] -name = "time" -version = "0.3.34" +name = "thiserror" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", - "time-macros", + "thiserror-impl", ] [[package]] -name = "time-core" -version = "0.1.2" +name = "thiserror-impl" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote 1.0.35", + "syn 1.0.91", +] [[package]] -name = "time-macros" -version = "0.2.17" +name = "time" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "211b63c112206356ef1ff9b19355f43740fc3f85960c598a93d3a3d3ba7beade" dependencies = [ - "num-conv", - "time-core", + "kernel32-sys", + "libc", + "redox_syscall", + "winapi 0.2.4", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "time" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" dependencies = [ - "tinyvec_macros", + "itoa 1.0.1", + "libc", + "num_threads", + "time-macros", ] [[package]] -name = "tinyvec_macros" -version = "0.1.1" +name = "time-macros" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" [[package]] name = "tokio" -version = "1.36.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -1021,7 +995,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1031,8 +1005,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", - "quote", - "syn 2.0.50", + "quote 1.0.35", + "syn 2.0.46", ] [[package]] @@ -1048,9 +1022,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "e4cdeb73537e63f98adcd73138af75e3f368ccaecffaa29d7eb61b9f5a440457" dependencies = [ "futures-core", "pin-project-lite", @@ -1072,36 +1046,48 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "2560b941fdb9ea38301b9b708504d612fcdf9c91a8c31d82219bd74cb07d304d" +dependencies = [ + "matches", +] [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] +checksum = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "untrusted" @@ -1111,12 +1097,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "77ddaf52e65c6b81c56b7e957c0b1970f7937f21c5c6774c4e56fcb4e20b48c6" dependencies = [ - "form_urlencoded", "idna", + "matches", "percent-encoding", ] @@ -1128,9 +1114,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "45d3d553fd9413fffe7147a20171d640eda0ad4c070acd7d0c885a21bcd2e8b7" [[package]] name = "wasi" @@ -1139,89 +1125,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.50", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.50", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.91" +name = "winapi" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "5350e40d908c7e8b9e5c9edb541ca47cc617c6229d3575a46da6f550f36c96fd" [[package]] name = "winapi" -version = "0.3.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "b3ad91d846a4a5342c1fb7008d26124ee6cf94a3953751618577295373b32117" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "winapi-build" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "winapi-i686-pc-windows-gnu" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "a16a8e2ebfc883e2b1771c6482b1fb3c6831eab289ba391619a2d93a7356220f" [[package]] -name = "windows-core" -version = "0.52.0" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.0", -] +checksum = "8ca29cb03c8ceaf20f8224a18a530938305e9872b1478ea24ff44b4f503a1d1d" [[package]] name = "windows-sys" @@ -1229,131 +1164,65 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "x509-parser" @@ -1369,11 +1238,11 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time", + "time 0.3.7", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml index ad404475..5d8a1a88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,12 @@ tokio = { version = "1.35.1", features = ["rt", "macros"] } tokio-test = "0.4.3" x509-parser = "0.15.1" +# to make -Zminimal-versions work +[target.'cfg(test)'.dependencies] +rustls = "0.22.1" +num-bigint = "0.4.2" +oid-registry = "0.6.1" + [[bin]] name = "loadtest" path = "src/bin/loadtest.rs" From 7ab8ab30f8dbcc5a093cf8aea908b37cda79e500 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sat, 24 Feb 2024 18:17:16 +0500 Subject: [PATCH 004/129] Comment out Openssl installation on windows in CI --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4adb2d78..b42d5e73 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -110,10 +110,10 @@ jobs: os: [macos-latest, windows-latest] steps: # if your project needs OpenSSL, uncomment this to fix Windows builds. - - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append - if: runner.os == 'Windows' - - run: vcpkg install openssl:x64-windows-static-md - if: runner.os == 'Windows' + # - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append + # if: runner.os == 'Windows' + # - run: vcpkg install openssl:x64-windows-static-md + # if: runner.os == 'Windows' - uses: actions/checkout@v4 with: submodules: true From e6b3a4c3c0e702680de961ca6f97e986386d852d Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sat, 24 Feb 2024 18:21:05 +0500 Subject: [PATCH 005/129] Revert "Comment out Openssl installation on windows in CI" This reverts commit 7ab8ab30f8dbcc5a093cf8aea908b37cda79e500. --- .github/workflows/test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b42d5e73..4adb2d78 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -110,10 +110,10 @@ jobs: os: [macos-latest, windows-latest] steps: # if your project needs OpenSSL, uncomment this to fix Windows builds. - # - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append - # if: runner.os == 'Windows' - # - run: vcpkg install openssl:x64-windows-static-md - # if: runner.os == 'Windows' + - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append + if: runner.os == 'Windows' + - run: vcpkg install openssl:x64-windows-static-md + if: runner.os == 'Windows' - uses: actions/checkout@v4 with: submodules: true From ab38bfc300d1e914ccdcb6d6a0717286568b0bf4 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sat, 24 Feb 2024 22:37:10 +0500 Subject: [PATCH 006/129] Restore patch version. Comment out OpenSSL install on CI --- .github/workflows/test.yml | 8 +- Cargo.lock | 908 +++++++++++++++++++++---------------- Cargo.toml | 2 +- 3 files changed, 526 insertions(+), 392 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4adb2d78..b42d5e73 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -110,10 +110,10 @@ jobs: os: [macos-latest, windows-latest] steps: # if your project needs OpenSSL, uncomment this to fix Windows builds. - - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append - if: runner.os == 'Windows' - - run: vcpkg install openssl:x64-windows-static-md - if: runner.os == 'Windows' + # - run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append + # if: runner.os == 'Windows' + # - run: vcpkg install openssl:x64-windows-static-md + # if: runner.os == 'Windows' - uses: actions/checkout@v4 with: submodules: true diff --git a/Cargo.lock b/Cargo.lock index e576899b..0c1dd7f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,24 +4,39 @@ version = 3 [[package]] name = "addr2line" -version = "0.14.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" -version = "0.2.1" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aa100a6f6f525226719f8de3f70076be4f4191801ebd92621450d1c51e9053d" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] [[package]] name = "anstream" -version = "0.6.0" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff2cf94a3dbe2d57cbd56485e1bd7436455058034d6c2d47be51d4e5e4bc6ab" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", @@ -33,63 +48,63 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "asn1-rs" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e577111f9ca51289da894bcb4b17047737218c2e4477ea2fc36cd3922172062f" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", "displaydoc", "nom", - "num-traits 0.2.14", + "num-traits", "rusticata-macros", "thiserror", - "time 0.3.7", + "time", ] [[package]] name = "asn1-rs-derive" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9002415e8baa0177a3ae0946fb62ca6e9e470755717409134e44d8e0ae2cad" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 1.0.91", + "quote", + "syn 1.0.109", "synstructure", ] @@ -100,29 +115,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 1.0.91", + "quote", + "syn 1.0.109", ] [[package]] name = "async-stream" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" dependencies = [ "async-stream-impl", "futures-core", + "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 1.0.91", + "quote", + "syn 2.0.50", ] [[package]] @@ -132,25 +148,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.46", + "quote", + "syn 2.0.50", ] [[package]] name = "autocfg" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.58" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88fb5a785d6b44fd9d6700935608639af1b8356de1e55d5f7c2740f4faa15d82" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object", @@ -159,30 +175,30 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.1" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] -name = "bytes" -version = "1.0.0" +name = "bumpalo" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72" +checksum = "c764d619ca78fccbf3069b37bd7af92577f044bb15236036662d79b6559f25b7" [[package]] -name = "cc" -version = "1.0.69" +name = "bytes" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] -name = "cfg-if" -version = "0.1.2" +name = "cc" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" +checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" [[package]] name = "cfg-if" @@ -192,42 +208,43 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.2" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ - "num-integer", - "num-traits 0.2.14", + "android-tzdata", + "iana-time-zone", + "num-traits", "serde", - "time 0.1.36", + "windows-targets 0.52.0", ] [[package]] name = "clap" -version = "4.4.10" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.9" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" @@ -235,29 +252,36 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crypto-common" -version = "0.1.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "typenum", ] [[package]] name = "darling" -version = "0.14.0" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a5d2e8b5a94b2261efb20e99a01255b9c5293797d69bbf04600567b2f9b8d7" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -265,49 +289,58 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.0" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1c7d56716be82d9c6adb967cfe700955179ea88806e898483dad6987330a54" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote 1.0.35", - "strsim", - "syn 1.0.91", + "quote", + "strsim 0.10.0", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.0" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64dd7e5a75a00cb6799ae9fbbfc3bba0134def6579a9e27564e72c839c837bed" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", - "quote 1.0.35", - "syn 1.0.91", + "quote", + "syn 1.0.109", ] [[package]] name = "data-encoding" -version = "2.2.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "der-parser" -version = "8.1.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" dependencies = [ "asn1-rs", "displaydoc", "nom", "num-bigint", - "num-traits 0.2.14", + "num-traits", "rusticata-macros", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_builder" version = "0.12.0" @@ -325,8 +358,8 @@ checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling", "proc-macro2", - "quote 1.0.35", - "syn 1.0.91", + "quote", + "syn 1.0.109", ] [[package]] @@ -336,14 +369,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ "derive_builder_core", - "syn 1.0.91", + "syn 1.0.109", ] [[package]] name = "digest" -version = "0.10.2" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -351,21 +384,15 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278ef1934318d524612205f69df005eea30ec10edf7913e500b5a527fce55bc0" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 1.0.91", + "quote", + "syn 2.0.50", ] -[[package]] -name = "dtoa" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" - [[package]] name = "faktory" version = "0.12.5" @@ -400,17 +427,26 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-core" -version = "0.3.0" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30f0ab78f035d7ed5d52689f4b05a56c15ad80097f1d860e644bdc9dba3831f2" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -418,30 +454,59 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" -version = "0.23.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hermit-abi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "hostname" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b1af8d6d068ba9de1c39c6ff0d879aed20f74873d4d3929a4535000bb07886" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" dependencies = [ "libc", "match_cfg", - "winapi 0.3.0", + "winapi", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", ] [[package]] @@ -452,35 +517,27 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.2.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "itoa" -version = "0.3.0" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fd9dc2c587067de817fec4ad355e3818c3d893a78cab32a0a474c7a15bb8d5" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] -name = "itoa" -version = "1.0.1" +name = "js-sys" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - -[[package]] -name = "kernel32-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1ca084b49bfd975182288e1a5f1d27ea34ff2d6ae084ae5e66e1652427eada" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ - "winapi 0.2.4", - "winapi-build", + "wasm-bindgen", ] [[package]] @@ -491,18 +548,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.149" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" -version = "0.4.4" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cba860f648db8e6f269df990180c2217f333472b4a6e901e97446858487971e2" -dependencies = [ - "cfg-if 0.1.2", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "match_cfg" @@ -510,118 +564,102 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -[[package]] -name = "matches" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e" - [[package]] name = "memchr" -version = "2.0.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01e64d9017d18e7fc09d8e4fe0e28ff6931019e979fb8019319db7ca827f8a6" -dependencies = [ - "libc", -] +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "minimal-lexical" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.4.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "nom" -version = "7.0.0" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] [[package]] name = "num-bigint" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.14", + "num-traits", ] [[package]] -name = "num-integer" -version = "0.1.42" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" -dependencies = [ - "autocfg", - "num-traits 0.2.14", -] +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] -name = "num-traits" -version = "0.1.32" +name = "num-integer" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51eab148f171aefad295f8cece636fc488b9b392ef544da31ea4b8ef6b9e9c39" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.8.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ + "hermit-abi", "libc", ] [[package]] -name = "num_threads" -version = "0.1.2" +name = "object" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ - "libc", + "memchr", ] -[[package]] -name = "object" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" - [[package]] name = "oid-registry" version = "0.6.1" @@ -631,11 +669,17 @@ dependencies = [ "asn1-rs", ] +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "percent-encoding" -version = "2.0.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f28a6faf4ffea762ba8f4baef48c61a6db348647c73095034041fc79dd954" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" @@ -653,37 +697,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.46", + "quote", + "syn 2.0.50", ] [[package]] name = "pin-project-lite" -version = "0.2.11" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.8" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.74" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] -[[package]] -name = "quote" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f0fc799e40f2a2c2be239825b30b686f1bd1d2e0e3d5e943b14c1380db49acf" - [[package]] name = "quote" version = "1.0.35" @@ -695,21 +739,20 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.0" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76330fb486679b4ace3670f117bbc9e16204005c4bde9c4bd372f45bed34f12" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", - "rand_hc", ] [[package]] name = "rand_chacha" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", @@ -717,62 +760,48 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35a48131ab10dbeb17202bd1dcb9c9798963a58a50c9ec31640f237358832094" - [[package]] name = "ring" -version = "0.17.0" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb9d44f9bf6b635117787f72416783eb7e4227aaf255e5ce739563d817176a7e" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", + "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustc-demangle" -version = "0.1.4" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rusticata-macros" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c52377bb2288aa522a0c8208947fada1e0c76397f108cc08f57efe6077b50d" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ "nom", ] [[package]] name = "rustls" -version = "0.22.1" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" dependencies = [ "log", "ring", @@ -784,15 +813,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.0.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb0a1f9b9efec70d32e6d6aa3e58ebd88c3754ec98dfe9145c63cf54cc829b83" +checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" [[package]] name = "rustls-webpki" -version = "0.102.0" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring", "rustls-pki-types", @@ -800,51 +829,49 @@ dependencies = [ ] [[package]] -name = "serde" -version = "1.0.0" +name = "ryu" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369633cfe0f0bde1dfc037fb6c5a329d46586a31f981bed14d87487a3439ae37" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] -name = "serde_derive" -version = "1.0.0" +name = "serde" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a61ecb8511aaff381424f98b49a059017420ec60e15e8d63b645701af7fa9b8" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ - "quote 0.3.8", - "serde_derive_internals", - "syn 0.11.10", + "serde_derive", ] [[package]] -name = "serde_derive_internals" -version = "0.15.0" +name = "serde_derive" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021c338d22c7e30f957a6ab7e388cb6098499dda9fd4ba1661ee074ca7a180d1" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ - "syn 0.11.10", - "synom", + "proc-macro2", + "quote", + "syn 2.0.50", ] [[package]] name = "serde_json" -version = "1.0.0" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9b1ec939469a124b27e208106550c38358ed4334d2b1b5b3825bc1ee37d946a" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ - "dtoa", - "itoa 0.3.0", - "num-traits 0.1.32", + "itoa", + "ryu", "serde", ] [[package]] name = "sha2" -version = "0.10.1" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest", ] @@ -856,14 +883,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "spin" -version = "0.9.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "strsim" @@ -871,6 +898,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "subtle" version = "2.5.0" @@ -879,113 +912,109 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "0.11.10" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171b739972d9a1bfb169e8077238b51f9ebeaae4ff6e08072f7ba386a8802da2" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "quote 0.3.8", - "synom", - "unicode-xid 0.0.4", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] name = "syn" -version = "1.0.91" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", - "quote 1.0.35", - "unicode-xid 0.2.0", + "quote", + "unicode-ident", ] [[package]] -name = "syn" -version = "2.0.46" +name = "synstructure" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", - "quote 1.0.35", - "unicode-ident", + "quote", + "syn 1.0.109", + "unicode-xid", ] [[package]] -name = "synom" -version = "0.11.0" +name = "thiserror" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fece1853fb872b0acdc3ff88f37c474018e125ef81cd4cb8c0ca515746b62ed" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ - "unicode-xid 0.0.4", + "thiserror-impl", ] [[package]] -name = "synstructure" -version = "0.12.0" +name = "thiserror-impl" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affc27d5f1764f7487bafeb41e380664790716e38ba45d8487bddcc53e79f0f6" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 1.0.91", - "unicode-xid 0.1.0", + "quote", + "syn 2.0.50", ] [[package]] -name = "thiserror" -version = "1.0.30" +name = "time" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ - "thiserror-impl", + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", ] [[package]] -name = "thiserror-impl" -version = "1.0.30" +name = "time-core" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" -dependencies = [ - "proc-macro2", - "quote 1.0.35", - "syn 1.0.91", -] +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] -name = "time" -version = "0.1.36" +name = "time-macros" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "211b63c112206356ef1ff9b19355f43740fc3f85960c598a93d3a3d3ba7beade" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ - "kernel32-sys", - "libc", - "redox_syscall", - "winapi 0.2.4", + "num-conv", + "time-core", ] [[package]] -name = "time" -version = "0.3.7" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "itoa 1.0.1", - "libc", - "num_threads", - "time-macros", + "tinyvec_macros", ] [[package]] -name = "time-macros" -version = "0.2.3" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -995,7 +1024,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1005,8 +1034,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", - "quote 1.0.35", - "syn 2.0.46", + "quote", + "syn 2.0.50", ] [[package]] @@ -1022,9 +1051,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.1" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4cdeb73537e63f98adcd73138af75e3f368ccaecffaa29d7eb61b9f5a440457" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -1046,48 +1075,36 @@ dependencies = [ [[package]] name = "typenum" -version = "1.12.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-bidi" -version = "0.3.0" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2560b941fdb9ea38301b9b708504d612fcdf9c91a8c31d82219bd74cb07d304d" -dependencies = [ - "matches", -] +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" - -[[package]] -name = "unicode-xid" -version = "0.0.4" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "untrusted" @@ -1097,12 +1114,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.0.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ddaf52e65c6b81c56b7e957c0b1970f7937f21c5c6774c4e56fcb4e20b48c6" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ + "form_urlencoded", "idna", - "matches", "percent-encoding", ] @@ -1114,9 +1131,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "version_check" -version = "0.9.0" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45d3d553fd9413fffe7147a20171d640eda0ad4c070acd7d0c885a21bcd2e8b7" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" @@ -1125,38 +1142,89 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "winapi" -version = "0.2.4" +name = "wasm-bindgen" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.50", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5350e40d908c7e8b9e5c9edb541ca47cc617c6229d3575a46da6f550f36c96fd" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "winapi" -version = "0.3.0" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ad91d846a4a5342c1fb7008d26124ee6cf94a3953751618577295373b32117" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] -name = "winapi-build" -version = "0.1.1" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.3.0" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16a8e2ebfc883e2b1771c6482b1fb3c6831eab289ba391619a2d93a7356220f" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.3.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca29cb03c8ceaf20f8224a18a530938305e9872b1478ea24ff44b4f503a1d1d" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] [[package]] name = "windows-sys" @@ -1164,65 +1232,131 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "x509-parser" @@ -1238,11 +1372,11 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.7", + "time", ] [[package]] name = "zeroize" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index 5d8a1a88..256a7e26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ tokio-test = "0.4.3" x509-parser = "0.15.1" # to make -Zminimal-versions work -[target.'cfg(test)'.dependencies] +[target.'cfg(any())'.dependencies] rustls = "0.22.1" num-bigint = "0.4.2" oid-registry = "0.6.1" From ce4c8f2412346c458d8be45b16e0f41ed317227a Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sat, 24 Feb 2024 23:45:19 +0500 Subject: [PATCH 007/129] Update tls test to use job runner --- tests/tls.rs | 161 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 99 insertions(+), 62 deletions(-) diff --git a/tests/tls.rs b/tests/tls.rs index e851a0cd..893283d3 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -3,7 +3,7 @@ use faktory::*; use serde_json::Value; use std::{ - env, io, + env, sync::{self, Arc}, }; use tokio_rustls::rustls::ClientConfig; @@ -27,18 +27,10 @@ async fn roundtrip_tls() { let local = "roundtrip_tls"; let (tx, rx) = sync::mpsc::channel(); - let tx = sync::Arc::new(sync::Mutex::new(tx)); let mut c = ConsumerBuilder::default(); + c.hostname("tester".to_string()).wid(local.to_string()); - { - c.register(local, move |j| { - let tx = sync::Arc::clone(&tx); - Box::pin(async move { - tx.lock().unwrap().send(j).unwrap(); - Ok::<(), io::Error>(()) - }) - }); - } + c.register_runner(local, fixtures::JobHandler::new(tx)); let tls = || async { let verifier = fixtures::TestServerCertVerifier::new( @@ -76,68 +68,113 @@ async fn roundtrip_tls() { } mod fixtures { - #![allow(unused_variables)] + pub use handler::JobHandler; + pub use tls::TestServerCertVerifier; - use std::fs; - use std::path::PathBuf; + mod handler { + use faktory::*; - use tokio_rustls::rustls::client::danger::{ - HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, - }; - use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime}; - use tokio_rustls::rustls::DigitallySignedStruct; - use tokio_rustls::rustls::Error as RustlsError; - use tokio_rustls::rustls::SignatureScheme; - use x509_parser::pem::parse_x509_pem; - - #[derive(Debug)] - pub(super) struct TestServerCertVerifier<'a> { - scheme: SignatureScheme, - cert_der: CertificateDer<'a>, - } + use std::{ + io, + sync::{mpsc::Sender, Arc, Mutex}, + time::Duration, + }; + use tokio::time; - impl TestServerCertVerifier<'_> { - pub(super) fn new(scheme: SignatureScheme, cert_path: PathBuf) -> Self { - let cert = fs::read(&cert_path).unwrap(); - let (_, pem) = parse_x509_pem(&cert).unwrap(); - let cert_der = CertificateDer::try_from(pem.contents).unwrap(); - Self { scheme, cert_der } + pub struct JobHandler { + chan: Arc>>, } - } - impl ServerCertVerifier for TestServerCertVerifier<'_> { - fn verify_server_cert( - &self, - end_entity: &CertificateDer<'_>, - intermediates: &[CertificateDer<'_>], - server_name: &ServerName<'_>, - ocsp_response: &[u8], - now: UnixTime, - ) -> Result { - assert_eq!(&self.cert_der, end_entity); - Ok(ServerCertVerified::assertion()) + impl JobHandler { + pub fn new(chan: Sender) -> Self { + Self { + chan: Arc::new(Mutex::new(chan)), + } + } + + async fn process_one(&self, job: Job) -> io::Result<()> { + time::sleep(Duration::from_millis(100)).await; + eprintln!("{:?}", job); + self.chan.lock().unwrap().send(job).unwrap(); + Ok(()) + } } - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) + #[async_trait::async_trait] + impl JobRunner for JobHandler { + type Error = io::Error; + + async fn run(&self, job: Job) -> Result<(), Self::Error> { + self.process_one(job).await.unwrap(); + Ok(()) + } + } + } + + mod tls { + #![allow(unused_variables)] + + use std::fs; + use std::path::PathBuf; + + use tokio_rustls::rustls::client::danger::{ + HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, + }; + use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime}; + use tokio_rustls::rustls::DigitallySignedStruct; + use tokio_rustls::rustls::Error as RustlsError; + use tokio_rustls::rustls::SignatureScheme; + use x509_parser::pem::parse_x509_pem; + + #[derive(Debug)] + pub struct TestServerCertVerifier<'a> { + scheme: SignatureScheme, + cert_der: CertificateDer<'a>, } - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) + impl TestServerCertVerifier<'_> { + pub fn new(scheme: SignatureScheme, cert_path: PathBuf) -> Self { + let cert = fs::read(&cert_path).unwrap(); + let (_, pem) = parse_x509_pem(&cert).unwrap(); + let cert_der = CertificateDer::try_from(pem.contents).unwrap(); + Self { scheme, cert_der } + } } - fn supported_verify_schemes(&self) -> Vec { - vec![self.scheme] + impl ServerCertVerifier for TestServerCertVerifier<'_> { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + assert_eq!(&self.cert_der, end_entity); + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![self.scheme] + } } } } From fb2ddf114daecbb654f01178f6639294342c8d40 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sun, 25 Feb 2024 00:02:03 +0500 Subject: [PATCH 008/129] interim --- src/consumer/runner.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/consumer/runner.rs b/src/consumer/runner.rs index 6a181bc2..6a962440 100644 --- a/src/consumer/runner.rs +++ b/src/consumer/runner.rs @@ -97,3 +97,34 @@ where } pub(crate) type BoxedJobRunner = Box>; + +#[repr(transparent)] +pub struct Runner(pub F); + +#[async_trait::async_trait] +impl JobRunner for Runner +where + F: Send + Sync + Fn(Job) -> Fut, + Fut: Future> + Send, + E: 'static +{ + type Error = E; + async fn run(&self, job: Job) -> Result<(), E> { + (self.0)(job).await + } +} + +// macro_rules! runner { +// ($struct:ident, $cmd:expr) => { +// #[async_trait::async_trait] +// impl FaktoryCommand for $struct { +// async fn issue(&self, w: &mut W) -> Result<(), Error> { +// let c = format!("{} ", $cmd); +// w.write_all(c.as_bytes()).await?; +// let r = serde_json::to_vec(self).map_err(Error::Serialization)?; +// w.write_all(&r).await?; +// Ok(w.write_all(b"\r\n").await?) +// } +// } +// }; +// } From 2d4ef10d7242445a0ce11dbaa22d672f4bf8dba7 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sun, 25 Feb 2024 00:54:54 +0500 Subject: [PATCH 009/129] Improme consumer builder api --- src/consumer/builder.rs | 7 ++-- src/consumer/mod.rs | 31 +++++++++++--- src/consumer/runner.rs | 57 ++++---------------------- tests/consumer.rs | 60 +++++++++++---------------- tests/real/community.rs | 24 +++++------ tests/real/enterprise.rs | 88 +++++++++++++++++----------------------- 6 files changed, 110 insertions(+), 157 deletions(-) diff --git a/src/consumer/builder.rs b/src/consumer/builder.rs index 4b558425..9bbcbf9e 100644 --- a/src/consumer/builder.rs +++ b/src/consumer/builder.rs @@ -7,7 +7,6 @@ use crate::{ Error, Job, JobRunner, }; use std::future::Future; -use std::pin::Pin; use tokio::io::{AsyncRead, AsyncWrite, BufStream}; use tokio::net::TcpStream as TokioStream; @@ -77,10 +76,12 @@ impl ConsumerBuilder { /// /// Whenever a job whose type matches `kind` is fetched from the Faktory, the given handler /// function is called with that job as its argument. - pub fn register(&mut self, kind: K, handler: H) -> &mut Self + pub fn register(&mut self, kind: K, handler: H) -> &mut Self where K: Into, - H: Send + Sync + Fn(Job) -> Pin> + Send>> + 'static, + + H: Fn(Job) -> Fut + Send + Sync + 'static, + Fut: Future> + Send, { self.register_runner(kind, Closure(handler)); self diff --git a/src/consumer/mod.rs b/src/consumer/mod.rs index 2139bc76..bf2ad326 100644 --- a/src/consumer/mod.rs +++ b/src/consumer/mod.rs @@ -97,19 +97,23 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// /// # Examples /// -/// Create a worker with all default options, register a single handler (for the `foobar` job +/// Create a worker with all default options, register a single handler (for the `foo` job /// type), connect to the Faktory server, and start accepting jobs. /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::ConsumerBuilder; +/// use faktory::{ConsumerBuilder, Job}; /// use std::io; /// -/// let mut c = ConsumerBuilder::default(); -/// c.register("foobar", |job| Box::pin(async move { +/// async fn process_job(job: Job) -> io::Result<()> { /// println!("{:?}", job); -/// Ok::<(), io::Error>(()) -/// })); +/// Ok(()) +/// } +/// +/// let mut c = ConsumerBuilder::default(); +/// +/// c.register("foo", process_job); +/// /// let mut c = c.connect(None).await.unwrap(); /// if let Err(e) = c.run(&["default"]).await { /// println!("worker failed: {}", e); @@ -117,6 +121,21 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// # }); /// ``` /// +/// Handler can be inlined. +/// +/// ```no_run +/// # use faktory::ConsumerBuilder; +/// # use std::io; +/// let mut c = ConsumerBuilder::default(); +/// c.register("bar", |job| async move { +/// println!("{:?}", job); +/// Ok::<(), io::Error>(()) +/// }); +/// ``` +/// +/// You can also register anything that implements [`JobRunner`] to handle jobs +/// with [`register_runner`](ConsumerBuilder::register_runner). +/// pub struct Consumer { c: Client, worker_states: Arc, diff --git a/src/consumer/runner.rs b/src/consumer/runner.rs index 6a962440..0d6e7aa6 100644 --- a/src/consumer/runner.rs +++ b/src/consumer/runner.rs @@ -1,5 +1,5 @@ use crate::Job; -use std::{future::Future, pin::Pin}; +use std::future::Future; /// Implementations of this trait can be registered to run jobs in a `Consumer`. /// @@ -49,9 +49,10 @@ pub trait JobRunner: Send + Sync { // Implements JobRunner for a closure that takes a Job and returns a Result<(), E> #[async_trait::async_trait] -impl JobRunner for Box +impl JobRunner for Box where - F: Send + Sync + Fn(Job) -> Pin> + Send>>, + F: Send + Sync + Fn(Job) -> Fut, + Fut: Future> + Send, { type Error = E; async fn run(&self, job: Job) -> Result<(), E> { @@ -61,9 +62,10 @@ where // Additional Blanket Implementations #[async_trait::async_trait] -impl<'a, E, F> JobRunner for &'a F +impl<'a, E, F, Fut> JobRunner for &'a F where - F: Send + Sync + Fn(Job) -> Pin> + Send>>, + F: Send + Sync + Fn(Job) -> Fut, + Fut: Future> + Send, { type Error = E; async fn run(&self, job: Job) -> Result<(), E> { @@ -71,42 +73,14 @@ where } } -#[async_trait::async_trait] -impl<'a, E, F> JobRunner for &'a mut F -where - F: Send + Sync + Fn(Job) -> Pin> + Send>>, -{ - type Error = E; - async fn run(&self, job: Job) -> Result<(), E> { - (self as &F)(job).await - } -} - #[repr(transparent)] pub(crate) struct Closure(pub F); #[async_trait::async_trait] -impl JobRunner for Closure -where - F: Send + Sync + Fn(Job) -> Pin> + Send>>, -{ - type Error = E; - async fn run(&self, job: Job) -> Result<(), E> { - (self.0)(job).await - } -} - -pub(crate) type BoxedJobRunner = Box>; - -#[repr(transparent)] -pub struct Runner(pub F); - -#[async_trait::async_trait] -impl JobRunner for Runner +impl JobRunner for Closure where F: Send + Sync + Fn(Job) -> Fut, Fut: Future> + Send, - E: 'static { type Error = E; async fn run(&self, job: Job) -> Result<(), E> { @@ -114,17 +88,4 @@ where } } -// macro_rules! runner { -// ($struct:ident, $cmd:expr) => { -// #[async_trait::async_trait] -// impl FaktoryCommand for $struct { -// async fn issue(&self, w: &mut W) -> Result<(), Error> { -// let c = format!("{} ", $cmd); -// w.write_all(c.as_bytes()).await?; -// let r = serde_json::to_vec(self).map_err(Error::Serialization)?; -// w.write_all(&r).await?; -// Ok(w.write_all(b"\r\n").await?) -// } -// } -// }; -// } +pub(crate) type BoxedJobRunner = Box>; diff --git a/tests/consumer.rs b/tests/consumer.rs index 3505dcac..1ddca0c6 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -15,7 +15,7 @@ async fn hello() { c.hostname("host".to_string()) .wid("wid".to_string()) .labels(vec!["foo".to_string(), "bar".to_string()]); - c.register("never_called", |_| Box::pin(async move { unreachable!() })); + c.register("never_called", |_j: Job| async move { unreachable!() }); let c = c.connect_with(s.clone(), None).await.unwrap(); let written = s.pop_bytes_written(0); assert!(written.starts_with(b"HELLO {")); @@ -41,7 +41,7 @@ async fn hello_pwd() { let mut s = mock::Stream::with_salt(1545, "55104dc76695721d"); let mut c: ConsumerBuilder = ConsumerBuilder::default(); - c.register("never_called", |_| Box::pin(async move { unreachable!() })); + c.register("never_called", |_j: Job| async move { unreachable!() }); let c = c .connect_with(s.clone(), Some("foobar".to_string())) .await @@ -62,11 +62,9 @@ async fn hello_pwd() { async fn dequeue() { let mut s = mock::Stream::default(); let mut c = ConsumerBuilder::default(); - c.register("foobar", |job: Job| { - Box::pin(async move { - assert_eq!(job.args(), &["z"]); - Ok::<(), io::Error>(()) - }) + c.register("foobar", |job: Job| async move { + assert_eq!(job.args(), &["z"]); + Ok::<(), io::Error>(()) }); let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); @@ -103,11 +101,9 @@ async fn dequeue() { async fn dequeue_first_empty() { let mut s = mock::Stream::default(); let mut c = ConsumerBuilder::default(); - c.register("foobar", |job: Job| { - Box::pin(async move { - assert_eq!(job.args(), &["z"]); - Ok::<(), io::Error>(()) - }) + c.register("foobar", |job: Job| async move { + assert_eq!(job.args(), &["z"]); + Ok::<(), io::Error>(()) }); let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); @@ -161,12 +157,10 @@ async fn well_behaved() { let mut s = mock::Stream::new(2); // main plus worker let mut c = ConsumerBuilder::default(); c.wid("wid".to_string()); - c.register("foobar", |_| { - Box::pin(async move { - // NOTE: this time needs to be so that it lands between the first heartbeat and the second - sleep(Duration::from_secs(7)).await; - Ok::<(), io::Error>(()) - }) + c.register("foobar", |_| async move { + // NOTE: this time needs to be so that it lands between the first heartbeat and the second + sleep(Duration::from_secs(7)).await; + Ok::<(), io::Error>(()) }); let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); @@ -228,12 +222,10 @@ async fn no_first_job() { let mut s = mock::Stream::new(2); let mut c = ConsumerBuilder::default(); c.wid("wid".to_string()); - c.register("foobar", |_| { - Box::pin(async move { - // NOTE: this time needs to be so that it lands between the first heartbeat and the second - sleep(Duration::from_secs(7)).await; - Ok::<(), io::Error>(()) - }) + c.register("foobar", |_| async move { + // NOTE: this time needs to be so that it lands between the first heartbeat and the second + sleep(Duration::from_secs(7)).await; + Ok::<(), io::Error>(()) }); let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); @@ -297,12 +289,10 @@ async fn well_behaved_many() { let mut c = ConsumerBuilder::default(); c.workers(2); c.wid("wid".to_string()); - c.register("foobar", |_| { - Box::pin(async move { - // NOTE: this time needs to be so that it lands between the first heartbeat and the second - sleep(Duration::from_secs(7)).await; - Ok::<(), io::Error>(()) - }) + c.register("foobar", |_| async move { + // NOTE: this time needs to be so that it lands between the first heartbeat and the second + sleep(Duration::from_secs(7)).await; + Ok::<(), io::Error>(()) }); let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); @@ -374,12 +364,10 @@ async fn terminate() { let mut s = mock::Stream::new(2); let mut c: ConsumerBuilder = ConsumerBuilder::default(); c.wid("wid".to_string()); - c.register("foobar", |_| { - Box::pin(async move { - loop { - sleep(Duration::from_secs(5)).await; - } - }) + c.register("foobar", |_| async move { + loop { + sleep(Duration::from_secs(5)).await; + } }); let mut c = c.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); diff --git a/tests/real/community.rs b/tests/real/community.rs index 13b9ce21..8f3248de 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -30,13 +30,11 @@ async fn roundtrip() { skip_check!(); let jid = String::from("x-job-id-0123456782"); let mut c = ConsumerBuilder::default(); - c.register("order", |job| Box::pin(process_order(job))); - c.register("image", |job| { - Box::pin(async move { - println!("{:?}", job); - assert_eq!(job.kind(), "image"); - Ok(()) - }) + c.register("order", process_order); + c.register("image", |job| async move { + println!("{:?}", job); + assert_eq!(job.kind(), "image"); + Ok(()) }); let mut c = c.connect(None).await.unwrap(); let mut p = Producer::connect(None).await.unwrap(); @@ -237,12 +235,10 @@ async fn test_jobs_pushed_in_bulk() { // is not an all-or-nothing operation: let mut c = ConsumerBuilder::default(); c.hostname("tester".to_string()).wid(local_3.to_string()); - c.register("very_special", move |_job| { - Box::pin(async move { Ok::<(), io::Error>(()) }) - }); - c.register("broken", move |_job| { - Box::pin(async move { Ok::<(), io::Error>(()) }) + c.register("very_special", move |_job| async { + Ok::<(), io::Error>(()) }); + c.register("broken", move |_job| async { Ok::<(), io::Error>(()) }); let mut c = c.connect(None).await.unwrap(); // we targeted "very_special" jobs to "local_4" queue @@ -272,8 +268,8 @@ async fn test_jobs_created_with_builder() { // prepare a producer ("client" in Faktory terms) and consumer ("worker"): let mut producer = Producer::connect(None).await.unwrap(); let mut consumer = ConsumerBuilder::default(); - consumer.register("rebuild_index", |j| Box::pin(assert_args_empty(j))); - consumer.register("register_order", |j| Box::pin(assert_args_not_empty(j))); + consumer.register("rebuild_index", assert_args_empty); + consumer.register("register_order", assert_args_not_empty); let mut consumer = consumer.connect(None).await.unwrap(); diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index fa2f6b54..ef2ca46b 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -45,7 +45,7 @@ async fn ent_expiring_job() { // prepare a producer ("client" in Faktory terms) and consumer ("worker"): let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register("AnExpiringJob", |j| Box::pin(print_job(j))); + c.register("AnExpiringJob", print_job); let mut c = c.connect(Some(&url)).await.unwrap(); // prepare an expiring job: @@ -94,7 +94,7 @@ async fn ent_unique_job() { // prepare producer and consumer: let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register(job_type, |j| Box::pin(print_job(j))); + c.register(job_type, print_job); let mut c = c.connect(Some(&url)).await.unwrap(); // Reminder. Jobs are considered unique for kind + args + queue. @@ -210,20 +210,18 @@ async fn ent_unique_job_until_success() { // to work hard: let mut producer_a = Producer::connect(Some(&url1)).await.unwrap(); let mut consumer_a = ConsumerBuilder::default_async(); - consumer_a.register(job_type, |job| { - Box::pin(async move { - let args = job.args().to_owned(); - let mut args = args.iter(); - let diffuculty_level = args - .next() - .expect("job difficulty level is there") - .to_owned(); - let sleep_secs = - serde_json::from_value::(diffuculty_level).expect("a valid number"); - time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; - eprintln!("{:?}", job); - Ok::<(), io::Error>(()) - }) + consumer_a.register(job_type, |job| async move { + let args = job.args().to_owned(); + let mut args = args.iter(); + let diffuculty_level = args + .next() + .expect("job difficulty level is there") + .to_owned(); + let sleep_secs = + serde_json::from_value::(diffuculty_level).expect("a valid number"); + time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; + eprintln!("{:?}", job); + Ok::<(), io::Error>(()) }); let mut consumer_a = consumer_a.connect(Some(&url1)).await.unwrap(); let job = JobBuilder::new(job_type) @@ -292,20 +290,18 @@ async fn ent_unique_job_until_start() { let handle = tokio::spawn(async move { let mut producer_a = Producer::connect(Some(&url1)).await.unwrap(); let mut consumer_a = ConsumerBuilder::default_async(); - consumer_a.register(job_type, |job| { - Box::pin(async move { - let args = job.args().to_owned(); - let mut args = args.iter(); - let diffuculty_level = args - .next() - .expect("job difficulty level is there") - .to_owned(); - let sleep_secs = - serde_json::from_value::(diffuculty_level).expect("a valid number"); - time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; - eprintln!("{:?}", job); - Ok::<(), io::Error>(()) - }) + consumer_a.register(job_type, |job| async move { + let args = job.args().to_owned(); + let mut args = args.iter(); + let diffuculty_level = args + .next() + .expect("job difficulty level is there") + .to_owned(); + let sleep_secs = + serde_json::from_value::(diffuculty_level).expect("a valid number"); + time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; + eprintln!("{:?}", job); + Ok::<(), io::Error>(()) }); let mut consumer_a = consumer_a.connect(Some(&url1)).await.unwrap(); producer_a @@ -384,7 +380,7 @@ async fn ent_unique_job_bypass_unique_lock() { // let's consume three times from the queue to verify that the first two jobs // have been enqueued for real, while the last one has not. let mut c = ConsumerBuilder::default_async(); - c.register("order", |j| Box::pin(print_job(j))); + c.register("order", print_job); let mut c = c.connect(Some(&url)).await.unwrap(); assert!(c.run_one(0, &[queue_name]).await.unwrap()); @@ -564,10 +560,8 @@ async fn test_batch_of_jobs_can_be_initiated() { let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register("thumbnail", move |_job| { - Box::pin(async move { Ok::<(), io::Error>(()) }) - }); - c.register("clean_up", move |_job| Box::pin(async move { Ok(()) })); + c.register("thumbnail", |_job| async { Ok::<(), io::Error>(()) }); + c.register("clean_up", |_job| async { Ok(()) }); let mut c = c.connect(Some(&url)).await.unwrap(); let mut t = Client::connect(Some(&url)) .await @@ -703,9 +697,7 @@ async fn test_batches_can_be_nested() { // Set up 'producer', 'consumer', and 'tracker': let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register("jobtype", move |_job| { - Box::pin(async move { Ok::<(), io::Error>(()) }) - }); + c.register("jobtype", |_job| async { Ok::<(), io::Error>(()) }); let mut _c = c.connect(Some(&url)).await.unwrap(); let mut t = Client::connect(Some(&url)) .await @@ -805,10 +797,8 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { // prepare a producer, a consumer of 'order' jobs, and a tracker: let mut p = Producer::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); - c.register("order", move |_job| Box::pin(async move { Ok(()) })); - c.register("order_clean_up", move |_job| { - Box::pin(async move { Ok::<(), io::Error>(()) }) - }); + c.register("order", |_job| async { Ok(()) }); + c.register("order_clean_up", |_job| async { Ok::<(), io::Error>(()) }); let mut c = c.connect(Some(&url)).await.unwrap(); let mut t = Client::connect(Some(&url)).await.unwrap(); @@ -932,14 +922,12 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { assert_eq!(s.success_callback_state, CallbackState::Pending); let mut c = ConsumerBuilder::default(); - c.register(complete_cb_jobtype, |_job| Box::pin(async { Ok(()) })); - c.register(success_cb_jobtype, |_job| { - Box::pin(async { - Err(io::Error::new( - io::ErrorKind::Other, - "we want this one to fail to test the 'CallbackState' behavior", - )) - }) + c.register(complete_cb_jobtype, |_job| async { Ok(()) }); + c.register(success_cb_jobtype, |_job| async { + Err(io::Error::new( + io::ErrorKind::Other, + "we want this one to fail to test the 'CallbackState' behavior", + )) }); let mut c = c.connect(Some(&url)).await.unwrap(); From 05825142e713044d7b6874eeb8905d5361015b91 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sun, 25 Feb 2024 00:58:50 +0500 Subject: [PATCH 010/129] Run fmt --- src/consumer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/consumer/mod.rs b/src/consumer/mod.rs index bf2ad326..62bc550f 100644 --- a/src/consumer/mod.rs +++ b/src/consumer/mod.rs @@ -133,7 +133,7 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// }); /// ``` /// -/// You can also register anything that implements [`JobRunner`] to handle jobs +/// You can also register anything that implements [`JobRunner`] to handle jobs /// with [`register_runner`](ConsumerBuilder::register_runner). /// pub struct Consumer { From 7f4479828234415f7e7b2535df63bdd4e9d92fed Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sun, 25 Feb 2024 11:29:29 +0500 Subject: [PATCH 011/129] Split client mod --- src/proto/client/ent.rs | 48 +++++ src/proto/client/mod.rs | 337 ++++++++++++++++++++++++++++++++++ src/proto/client/options.rs | 39 ++++ src/proto/mod.rs | 351 +----------------------------------- 4 files changed, 430 insertions(+), 345 deletions(-) create mode 100644 src/proto/client/ent.rs create mode 100644 src/proto/client/mod.rs create mode 100644 src/proto/client/options.rs diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs new file mode 100644 index 00000000..fcf13a58 --- /dev/null +++ b/src/proto/client/ent.rs @@ -0,0 +1,48 @@ +use super::super::{single, BatchStatus, GetBatchStatus, Progress, ProgressUpdate, Track}; +use super::{Client, ReadToken}; +use crate::error::Error; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; + +impl Client { + /// Send information on a job's execution progress to Faktory. + pub async fn set_progress(&mut self, upd: ProgressUpdate) -> Result<(), Error> { + let cmd = Track::Set(upd); + self.issue(&cmd).await?.read_ok().await + } + + /// Fetch information on a job's execution progress from Faktory. + pub async fn get_progress(&mut self, jid: String) -> Result, Error> { + let cmd = Track::Get(jid); + self.issue(&cmd).await?.read_json().await + } + + /// Fetch information on a batch of jobs execution progress. + pub async fn get_batch_status(&mut self, bid: String) -> Result, Error> { + let cmd = GetBatchStatus::from(bid); + self.issue(&cmd).await?.read_json().await + } +} + +impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> ReadToken<'a, S> { + pub(crate) async fn read_bid(self) -> Result { + single::read_bid(&mut self.0.stream).await + } + + pub(crate) async fn maybe_bid(self) -> Result, Error> { + use crate::error; + + let bid_read_res = single::read_bid(&mut self.0.stream).await; + if bid_read_res.is_ok() { + return Ok(Some(bid_read_res.unwrap())); + } + match bid_read_res.unwrap_err() { + Error::Protocol(error::Protocol::Internal { msg }) => { + if msg.starts_with("No such batch") { + return Ok(None); + } + return Err(error::Protocol::Internal { msg }.into()); + } + another => Err(another), + } + } +} diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs new file mode 100644 index 00000000..3a5feb86 --- /dev/null +++ b/src/proto/client/mod.rs @@ -0,0 +1,337 @@ +#[cfg(doc)] +use crate::{Consumer, Producer}; + +#[cfg(feature = "ent")] +#[cfg_attr(docsrs, doc(cfg(feature = "ent")))] +mod ent; + +use super::utils; +use super::{single, Reconnect}; +use crate::error::{self, Error}; +use crate::Job; +use tokio::io::BufStream; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::net::TcpStream as TokioStream; + +mod options; +pub(crate) use options::ClientOptions; + +pub(crate) const EXPECTED_PROTOCOL_VERSION: usize = 2; + +fn check_protocols_match(ver: usize) -> Result<(), Error> { + if ver != EXPECTED_PROTOCOL_VERSION { + return Err(error::Connect::VersionMismatch { + ours: EXPECTED_PROTOCOL_VERSION, + theirs: ver, + } + .into()); + } + Ok(()) +} + +/// `Client` is used to enqueue new jobs that will in turn be processed by Faktory workers. +/// +/// # Connecting to Faktory +/// +/// To issue jobs, the `Client` must first be connected to the Faktory server. Exactly how you do +/// that depends on your setup. Faktory suggests using the `FAKTORY_PROVIDER` and `FAKTORY_URL` +/// environment variables (see their docs for more information) with `localhost:7419` as the +/// fallback default. If you want this behavior, pass `None` to [`Client::connect`](Client::connect). +/// If not, you can supply the URL directly in the form: +/// +/// ```text +/// protocol://[:password@]hostname[:port] +/// ``` +/// +/// +/// # Issuing jobs +/// +/// Most of the lifetime of a `Client` will be spent creating and enqueueing jobs for Faktory +/// workers. This is done by passing a [`Job`](struct.Job.html) to +/// [`Client::enqueue`](Client::enqueue). The most important part of a `Job` +/// is its `kind`; this field dictates how workers will execute the job when they receive it. The +/// string provided here must match a handler registered on the worker using +/// [`ConsumerBuilder::register`](struct.ConsumerBuilder.html#method.register) (or the equivalent +/// handler registration method in workers written in other languages). +/// +/// Since Faktory workers do not all need to be the same (you could have some written in Rust for +/// performance-critical tasks, some in Ruby for more webby tasks, etc.), it may be the case that a +/// given job can only be executed by some workers (e.g., if they job type is not registered at +/// others). To allow for this, Faktory includes a `labels` field with each job. Jobs will only be +/// sent to workers whose labels (see +/// [`ConsumerBuilder::labels`](struct.ConsumerBuilder.html#method.labels)) match those set in +/// `Job::labels`. +/// +/// # Examples +/// +/// Connecting to an unsecured Faktory server using environment variables: +/// +/// ```no_run +/// # tokio_test::block_on(async { +/// use faktory::Client; +/// let p = Client::connect(None).await.unwrap(); +/// # }); +/// ``` +/// +/// Connecting to a secured Faktory server using an explicit URL: +/// +/// ```no_run +/// # tokio_test::block_on(async { +/// use faktory::Client; +/// let p = Client::connect(Some("tcp://:hunter2@localhost:7439")).await.unwrap(); +/// # }) +/// ``` +/// +/// Issuing a job using a `Client`: +/// +/// ```no_run +/// # tokio_test::block_on(async { +/// # use faktory::Client; +/// # let mut client = Client::connect(None).await.unwrap(); +/// use faktory::Job; +/// client.enqueue(Job::new("foobar", vec!["z"])).await.unwrap(); +/// # }); +/// ``` +/// +/// `Client` is also useful for retrieving and updating information on a job's execution progress +/// (see [`Progress`] and [`ProgressUpdate`]), as well for retrieving a batch's status +/// from the Faktory server (see [`BatchStatus`]). +/// +/// Fetching a job's execution progress: +/// +/// ```no_run +/// # tokio_test::block_on(async { +/// use faktory::{Client, ent::JobState}; +/// let job_id = String::from("W8qyVle9vXzUWQOf"); +/// let mut cl = Client::connect(None).await?; +/// if let Some(progress) = cl.get_progress(job_id).await? { +/// if let JobState::Success = progress.state { +/// # /* +/// ... +/// # */ +/// } +/// } +/// # Ok::<(), faktory::Error>(()) +/// }); +/// ``` +/// +/// Sending an update on a job's execution progress: +/// +/// ```no_run +/// # tokio_test::block_on(async { +/// use faktory::{Client, ent::ProgressUpdateBuilder}; +/// let jid = String::from("W8qyVle9vXzUWQOf"); +/// let mut cl = Client::connect(None).await?; +/// let progress = ProgressUpdateBuilder::new(&jid) +/// .desc("Almost done...".to_owned()) +/// .percent(99) +/// .build(); +/// cl.set_progress(progress).await?; +/// # Ok::<(), faktory::Error>(()) +/// }); +///```` +/// +/// Fetching a batch's status: +/// +/// ```no_run +/// # tokio_test::block_on(async { +/// use faktory::Client; +/// let bid = String::from("W8qyVle9vXzUWQOg"); +/// let mut cl = Client::connect(None).await?; +/// if let Some(status) = cl.get_batch_status(bid).await? { +/// println!("This batch created at {}", status.created_at); +/// } +/// # Ok::<(), faktory::Error>(()) +/// }); +/// ``` +pub struct Client { + stream: S, + opts: ClientOptions, +} + +impl Client +where + S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send + Reconnect, +{ + pub(crate) async fn connect_again(&mut self) -> Result { + let s = self.stream.reconnect().await?; + Client::new(s, self.opts.clone()).await + } + + pub(crate) async fn reconnect(&mut self) -> Result<(), Error> { + self.stream = self.stream.reconnect().await?; + self.init().await + } +} + +impl Drop for Client +where + S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, +{ + fn drop(&mut self) { + tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(async { + single::write_command(&mut self.stream, &single::End) + .await + .unwrap(); + }) + }); + } +} + +pub(crate) enum HeartbeatStatus { + Ok, + Terminate, + Quiet, +} + +impl Client> { + /// Create new [`Client`] and connect to a Faktory server. + /// + /// If `url` is not given, will use the standard Faktory environment variables. Specifically, + /// `FAKTORY_PROVIDER` is read to get the name of the environment variable to get the address + /// from (defaults to `FAKTORY_URL`), and then that environment variable is read to get the + /// server address. If the latter environment variable is not defined, the connection will be + /// made to + /// + /// ```text + /// tcp://localhost:7419 + /// ``` + pub async fn connect(url: Option<&str>) -> Result>, Error> { + let url = utils::parse_provided_or_from_env(url)?; + let stream = TokioStream::connect(utils::host_from_url(&url)).await?; + let buffered = BufStream::new(stream); + Self::connect_with(buffered, url.password().map(|p| p.to_string())).await + } +} + +impl Client +where + S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, +{ + async fn init(&mut self) -> Result<(), Error> { + let hi = single::read_hi(&mut self.stream).await?; + check_protocols_match(hi.version)?; + + let mut hello = single::Hello::default(); + + // prepare password hash, if one expected by 'Faktory' + if hi.salt.is_some() { + if let Some(ref pwd) = self.opts.password { + hello.set_password(&hi, pwd); + } else { + return Err(error::Connect::AuthenticationNeeded.into()); + } + } + + // fill in any missing options, and remember them for re-connect + let mut hello = single::Hello::default(); + + if self.opts.is_worker { + // fill in any missing options, and remember them for re-connect + + let hostname = self + .opts + .hostname + .clone() + .or_else(|| hostname::get().ok()?.into_string().ok()) + .unwrap_or_else(|| "local".to_string()); + self.opts.hostname = Some(hostname); + let pid = self.opts.pid.unwrap_or_else(|| std::process::id() as usize); + self.opts.pid = Some(pid); + let wid = self.opts.wid.clone().unwrap_or_else(single::gen_random_wid); + self.opts.wid = Some(wid); + + hello.hostname = Some(self.opts.hostname.clone().unwrap()); + hello.wid = Some(self.opts.wid.clone().unwrap()); + hello.pid = Some(self.opts.pid.unwrap()); + hello.labels = self.opts.labels.clone(); + } + + if hi.salt.is_some() { + if let Some(ref pwd) = self.opts.password { + hello.set_password(&hi, pwd); + } else { + return Err(error::Connect::AuthenticationNeeded.into()); + } + } + + single::write_command_and_await_ok(&mut self.stream, &hello).await?; + Ok(()) + } + + pub(crate) async fn new(stream: S, opts: ClientOptions) -> Result, Error> { + let mut c = Client { stream, opts }; + c.init().await?; + Ok(c) + } + + /// Create new [`Client`] and connect to a Faktory server with a non-standard stream. + pub async fn connect_with(stream: S, pwd: Option) -> Result, Error> { + let opts = ClientOptions { + password: pwd, + ..Default::default() + }; + Client::new(stream, opts).await + } + + pub(crate) async fn issue( + &mut self, + c: &FC, + ) -> Result, Error> { + single::write_command(&mut self.stream, c).await?; + Ok(ReadToken(self)) + } + + pub(crate) async fn fetch(&mut self, queues: &[Q]) -> Result, Error> + where + Q: AsRef + Sync, + { + self.issue(&single::Fetch::from(queues)) + .await? + .read_json() + .await + } + + pub(crate) async fn heartbeat(&mut self) -> Result { + single::write_command( + &mut self.stream, + &single::Heartbeat::new(&**self.opts.wid.as_ref().unwrap()), + ) + .await?; + + match single::read_json::<_, serde_json::Value>(&mut self.stream).await? { + None => Ok(HeartbeatStatus::Ok), + Some(s) => match s + .as_object() + .and_then(|m| m.get("state")) + .and_then(|s| s.as_str()) + { + Some("terminate") => Ok(HeartbeatStatus::Terminate), + Some("quiet") => Ok(HeartbeatStatus::Quiet), + _ => Err(error::Protocol::BadType { + expected: "heartbeat response", + received: format!("{}", s), + } + .into()), + }, + } + } +} + +pub struct ReadToken<'a, S>(pub(crate) &'a mut Client) +where + S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send; + +impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> ReadToken<'a, S> { + pub(crate) async fn read_ok(self) -> Result<(), Error> { + single::read_ok(&mut self.0.stream).await + } + + pub(crate) async fn read_json(self) -> Result, Error> + where + T: serde::de::DeserializeOwned, + { + single::read_json(&mut self.0.stream).await + } +} diff --git a/src/proto/client/options.rs b/src/proto/client/options.rs new file mode 100644 index 00000000..67dc0a2a --- /dev/null +++ b/src/proto/client/options.rs @@ -0,0 +1,39 @@ +#[derive(Clone)] +pub(crate) struct ClientOptions { + /// Hostname to advertise to server. + /// Defaults to machine hostname. + pub(crate) hostname: Option, + + /// PID to advertise to server. + /// Defaults to process ID. + pub(crate) pid: Option, + + /// Worker ID to advertise to server. + /// Defaults to a GUID. + pub(crate) wid: Option, + + /// Labels to advertise to se/// A stream that can be re-established after failing.rver. + /// Defaults to ["rust"]. + pub(crate) labels: Vec, + + /// Password to authenticate with + /// Defaults to None. + pub(crate) password: Option, + + /// Whether this client is instatianted for + /// a consumer ("worker" in Faktory terms). + pub(crate) is_worker: bool, +} + +impl Default for ClientOptions { + fn default() -> Self { + ClientOptions { + hostname: None, + pid: None, + wid: None, + labels: vec!["rust".to_string()], + password: None, + is_worker: false, + } + } +} diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 0d7fe1a7..3db996dc 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -1,19 +1,19 @@ -#[cfg(doc)] -use crate::{Consumer, Producer}; - -use crate::error::{self, Error}; use std::io; use tokio::io::BufStream; -use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt}; +use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::TcpStream as TokioStream; +mod client; +pub use client::Client; +pub(crate) use client::{ClientOptions, HeartbeatStatus, EXPECTED_PROTOCOL_VERSION}; + mod single; + pub use single::{Ack, Fail, Info, Job, JobBuilder, Push, PushBulk, QueueAction, QueueControl}; pub(crate) mod utils; #[cfg(feature = "ent")] pub use self::single::ent::{JobState, Progress, ProgressUpdate, ProgressUpdateBuilder, Track}; -use self::single::Heartbeat; #[cfg(feature = "ent")] mod batch; @@ -23,59 +23,6 @@ pub use batch::{ OpenBatch, }; -pub(crate) const EXPECTED_PROTOCOL_VERSION: usize = 2; - -fn check_protocols_match(ver: usize) -> Result<(), Error> { - if ver != EXPECTED_PROTOCOL_VERSION { - return Err(error::Connect::VersionMismatch { - ours: EXPECTED_PROTOCOL_VERSION, - theirs: ver, - } - .into()); - } - Ok(()) -} - -#[derive(Clone)] -pub(crate) struct ClientOptions { - /// Hostname to advertise to server. - /// Defaults to machine hostname. - pub(crate) hostname: Option, - - /// PID to advertise to server. - /// Defaults to process ID. - pub(crate) pid: Option, - - /// Worker ID to advertise to server. - /// Defaults to a GUID. - pub(crate) wid: Option, - - /// Labels to advertise to se/// A stream that can be re-established after failing.rver. - /// Defaults to ["rust"]. - pub(crate) labels: Vec, - - /// Password to authenticate with - /// Defaults to None. - pub(crate) password: Option, - - /// Whether this client is instatianted for - /// a consumer ("worker" in Faktory terms). - pub(crate) is_worker: bool, -} - -impl Default for ClientOptions { - fn default() -> Self { - ClientOptions { - hostname: None, - pid: None, - wid: None, - labels: vec!["rust".to_string()], - password: None, - is_worker: false, - } - } -} - /// A stream that can be re-established after failing. #[async_trait::async_trait] pub trait Reconnect: Sized { @@ -102,289 +49,3 @@ where Ok(Self::new(stream)) } } - -/// A Faktory connection that represents neither a [`Producer`] nor a [`Consumer`]. -/// -/// Useful for retrieving and updating information on a job's execution progress -/// (see [`Progress`] and [`ProgressUpdate`]), as well for retrieving a batch's status -/// from the Faktory server (see [`BatchStatus`]). -/// -/// Fetching a job's execution progress: -/// ```no_run -/// # tokio_test::block_on(async { -/// use faktory::{Client, ent::JobState}; -/// let job_id = String::from("W8qyVle9vXzUWQOf"); -/// let mut cl = Client::connect(None).await?; -/// if let Some(progress) = cl.get_progress(job_id).await? { -/// if let JobState::Success = progress.state { -/// # /* -/// ... -/// # */ -/// } -/// } -/// # Ok::<(), faktory::Error>(()) -/// }); -/// ``` -/// -/// Sending an update on a job's execution progress: -/// -/// ```no_run -/// # tokio_test::block_on(async { -/// use faktory::{Client, ent::ProgressUpdateBuilder}; -/// let jid = String::from("W8qyVle9vXzUWQOf"); -/// let mut cl = Client::connect(None).await?; -/// let progress = ProgressUpdateBuilder::new(&jid) -/// .desc("Almost done...".to_owned()) -/// .percent(99) -/// .build(); -/// cl.set_progress(progress).await?; -/// # Ok::<(), faktory::Error>(()) -/// }); -///```` -/// -/// Fetching a batch's status: -/// -/// ```no_run -/// # tokio_test::block_on(async { -/// use faktory::Client; -/// let bid = String::from("W8qyVle9vXzUWQOg"); -/// let mut cl = Client::connect(None).await?; -/// if let Some(status) = cl.get_batch_status(bid).await? { -/// println!("This batch created at {}", status.created_at); -/// } -/// # Ok::<(), faktory::Error>(()) -/// }); -/// ``` -pub struct Client { - stream: S, - opts: ClientOptions, -} - -impl Client -where - S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send + Reconnect, -{ - pub(crate) async fn connect_again(&mut self) -> Result { - let s = self.stream.reconnect().await?; - Client::new(s, self.opts.clone()).await - } - - pub(crate) async fn reconnect(&mut self) -> Result<(), Error> { - self.stream = self.stream.reconnect().await?; - self.init().await - } -} - -impl Drop for Client -where - S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, -{ - fn drop(&mut self) { - tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(async { - single::write_command(&mut self.stream, &single::End) - .await - .unwrap(); - }) - }); - } -} - -pub(crate) enum HeartbeatStatus { - Ok, - Terminate, - Quiet, -} - -impl Client> { - /// Create new [`Client`] and connect to a Faktory server. - /// - /// If `url` is not given, will use the standard Faktory environment variables. Specifically, - /// `FAKTORY_PROVIDER` is read to get the name of the environment variable to get the address - /// from (defaults to `FAKTORY_URL`), and then that environment variable is read to get the - /// server address. If the latter environment variable is not defined, the connection will be - /// made to - /// - /// ```text - /// tcp://localhost:7419 - /// ``` - pub async fn connect(url: Option<&str>) -> Result>, Error> { - let url = utils::parse_provided_or_from_env(url)?; - let stream = TokioStream::connect(utils::host_from_url(&url)).await?; - let buffered = BufStream::new(stream); - Self::connect_with(buffered, url.password().map(|p| p.to_string())).await - } -} - -impl Client -where - S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, -{ - async fn init(&mut self) -> Result<(), Error> { - let hi = single::read_hi(&mut self.stream).await?; - check_protocols_match(hi.version)?; - - let mut hello = single::Hello::default(); - - // prepare password hash, if one expected by 'Faktory' - if hi.salt.is_some() { - if let Some(ref pwd) = self.opts.password { - hello.set_password(&hi, pwd); - } else { - return Err(error::Connect::AuthenticationNeeded.into()); - } - } - - // fill in any missing options, and remember them for re-connect - let mut hello = single::Hello::default(); - - if self.opts.is_worker { - // fill in any missing options, and remember them for re-connect - - let hostname = self - .opts - .hostname - .clone() - .or_else(|| hostname::get().ok()?.into_string().ok()) - .unwrap_or_else(|| "local".to_string()); - self.opts.hostname = Some(hostname); - let pid = self.opts.pid.unwrap_or_else(|| std::process::id() as usize); - self.opts.pid = Some(pid); - let wid = self.opts.wid.clone().unwrap_or_else(single::gen_random_wid); - self.opts.wid = Some(wid); - - hello.hostname = Some(self.opts.hostname.clone().unwrap()); - hello.wid = Some(self.opts.wid.clone().unwrap()); - hello.pid = Some(self.opts.pid.unwrap()); - hello.labels = self.opts.labels.clone(); - } - - if hi.salt.is_some() { - if let Some(ref pwd) = self.opts.password { - hello.set_password(&hi, pwd); - } else { - return Err(error::Connect::AuthenticationNeeded.into()); - } - } - - single::write_command_and_await_ok(&mut self.stream, &hello).await?; - Ok(()) - } - - pub(crate) async fn new(stream: S, opts: ClientOptions) -> Result, Error> { - let mut c = Client { stream, opts }; - c.init().await?; - Ok(c) - } - - /// Create new [`Client`] and connect to a Faktory server with a non-standard stream. - pub async fn connect_with(stream: S, pwd: Option) -> Result, Error> { - let opts = ClientOptions { - password: pwd, - ..Default::default() - }; - Client::new(stream, opts).await - } - - pub(crate) async fn issue( - &mut self, - c: &FC, - ) -> Result, Error> { - single::write_command(&mut self.stream, c).await?; - Ok(ReadToken(self)) - } - - pub(crate) async fn fetch(&mut self, queues: &[Q]) -> Result, Error> - where - Q: AsRef + Sync, - { - self.issue(&single::Fetch::from(queues)) - .await? - .read_json() - .await - } - - pub(crate) async fn heartbeat(&mut self) -> Result { - single::write_command( - &mut self.stream, - &Heartbeat::new(&**self.opts.wid.as_ref().unwrap()), - ) - .await?; - - match single::read_json::<_, serde_json::Value>(&mut self.stream).await? { - None => Ok(HeartbeatStatus::Ok), - Some(s) => match s - .as_object() - .and_then(|m| m.get("state")) - .and_then(|s| s.as_str()) - { - Some("terminate") => Ok(HeartbeatStatus::Terminate), - Some("quiet") => Ok(HeartbeatStatus::Quiet), - _ => Err(error::Protocol::BadType { - expected: "heartbeat response", - received: format!("{}", s), - } - .into()), - }, - } - } -} - -#[cfg(feature = "ent")] -#[cfg_attr(docsrs, doc(cfg(feature = "ent")))] -impl Client { - /// Send information on a job's execution progress to Faktory. - pub async fn set_progress(&mut self, upd: ProgressUpdate) -> Result<(), Error> { - let cmd = Track::Set(upd); - self.issue(&cmd).await?.read_ok().await - } - - /// Fetch information on a job's execution progress from Faktory. - pub async fn get_progress(&mut self, jid: String) -> Result, Error> { - let cmd = Track::Get(jid); - self.issue(&cmd).await?.read_json().await - } - - /// Fetch information on a batch of jobs execution progress. - pub async fn get_batch_status(&mut self, bid: String) -> Result, Error> { - let cmd = GetBatchStatus::from(bid); - self.issue(&cmd).await?.read_json().await - } -} - -pub struct ReadToken<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send>(&'a mut Client); - -impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> ReadToken<'a, S> { - pub(crate) async fn read_ok(self) -> Result<(), Error> { - single::read_ok(&mut self.0.stream).await - } - - pub(crate) async fn read_json(self) -> Result, Error> - where - T: serde::de::DeserializeOwned, - { - single::read_json(&mut self.0.stream).await - } - - #[cfg(feature = "ent")] - pub(crate) async fn read_bid(self) -> Result { - single::read_bid(&mut self.0.stream).await - } - - #[cfg(feature = "ent")] - pub(crate) async fn maybe_bid(self) -> Result, Error> { - let bid_read_res = single::read_bid(&mut self.0.stream).await; - if bid_read_res.is_ok() { - return Ok(Some(bid_read_res.unwrap())); - } - match bid_read_res.unwrap_err() { - Error::Protocol(error::Protocol::Internal { msg }) => { - if msg.starts_with("No such batch") { - return Ok(None); - } - return Err(error::Protocol::Internal { msg }.into()); - } - another => Err(another), - } - } -} From 84864da7ae5aa2e614a200dcbab4586540666393 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sun, 25 Feb 2024 18:59:22 +0500 Subject: [PATCH 012/129] Producer -> Client --- src/bin/loadtest.rs | 4 +- src/lib.rs | 10 +- src/producer/mod.rs | 218 --------------------------------------- src/proto/batch/mod.rs | 35 +++---- src/proto/client/ent.rs | 24 ++++- src/proto/client/mod.rs | 113 +++++++++++++++++--- src/proto/mod.rs | 3 +- src/tls.rs | 6 +- tests/producer.rs | 8 +- tests/real/community.rs | 26 ++--- tests/real/enterprise.rs | 28 ++--- tests/tls.rs | 2 +- 12 files changed, 177 insertions(+), 300 deletions(-) delete mode 100644 src/producer/mod.rs diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index 1b21368a..94adc72a 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -40,7 +40,7 @@ async fn main() { // ensure that we can actually connect to the server; // will create a client, run a handshake with Faktory, // and drop the cliet immediately afterwards; - if let Err(e) = Producer::connect(None).await { + if let Err(e) = Client::connect(None).await { println!("{}", e); process::exit(1); } @@ -55,7 +55,7 @@ async fn main() { let popped = sync::Arc::clone(&popped); tokio::spawn(async move { // make producer and consumer - let mut p = Producer::connect(None).await.unwrap(); + let mut p = Client::connect(None).await.unwrap(); let mut c = ConsumerBuilder::default(); c.register("SomeJob", |_| { Box::pin(async move { diff --git a/src/lib.rs b/src/lib.rs index 0da91716..9858cea6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,11 +35,11 @@ //! //! ```no_run //! # tokio_test::block_on(async { -//! use faktory::{Producer, Job}; -//! let mut p = Producer::connect(None).await.unwrap(); -//! p.enqueue(Job::new("foobar", vec!["z"])).await.unwrap(); +//! use faktory::{Client, Job}; +//! let mut client = Client::connect(None).await.unwrap(); +//! client.enqueue(Job::new("foobar", vec!["z"])).await.unwrap(); //! -//! let (enqueued_count, errors) = p.enqueue_many([Job::new("foobar", vec!["z"]), Job::new("foobar", vec!["z"])]).await.unwrap(); +//! let (enqueued_count, errors) = client.enqueue_many([Job::new("foobar", vec!["z"]), Job::new("foobar", vec!["z"])]).await.unwrap(); //! assert_eq!(enqueued_count, 2); //! assert_eq!(errors, None); //! }); @@ -71,12 +71,10 @@ extern crate serde_derive; pub mod error; mod consumer; -mod producer; mod proto; pub use crate::consumer::{Consumer, ConsumerBuilder, JobRunner}; pub use crate::error::Error; -pub use crate::producer::Producer; pub use crate::proto::{Client, Job, JobBuilder, Reconnect}; #[cfg(feature = "ent")] diff --git a/src/producer/mod.rs b/src/producer/mod.rs deleted file mode 100644 index 8589d816..00000000 --- a/src/producer/mod.rs +++ /dev/null @@ -1,218 +0,0 @@ -use std::collections::HashMap; - -use crate::proto::{Client, Info, Job, Push, PushBulk, QueueAction, QueueControl}; -use crate::Error; -use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufStream}; -use tokio::net::TcpStream as TokioStream; - -#[cfg(feature = "ent")] -use crate::proto::{Batch, BatchHandle, CommitBatch, OpenBatch}; - -/// `Producer` is used to enqueue new jobs that will in turn be processed by Faktory workers. -/// -/// # Connecting to Faktory -/// -/// To issue jobs, the `Producer` must first be connected to the Faktory server. Exactly how you do -/// that depends on your setup. Faktory suggests using the `FAKTORY_PROVIDER` and `FAKTORY_URL` -/// environment variables (see their docs for more information) with `localhost:7419` as the -/// fallback default. If you want this behavior, pass `None` to -/// [`Producer::connect`](struct.Producer.html#method.connect). If not, you can supply the URL -/// directly to [`Producer::connect`](struct.Producer.html#method.connect) in the form: -/// -/// ```text -/// protocol://[:password@]hostname[:port] -/// ``` -/// -/// -/// # Issuing jobs -/// -/// Most of the lifetime of a `Producer` will be spent creating and enqueueing jobs for Faktory -/// workers. This is done by passing a [`Job`](struct.Job.html) to -/// [`Producer::enqueue`](struct.Producer.html#method.enqueue). The most important part of a `Job` -/// is its `kind`; this field dictates how workers will execute the job when they receive it. The -/// string provided here must match a handler registered on the worker using -/// [`ConsumerBuilder::register`](struct.ConsumerBuilder.html#method.register) (or the equivalent -/// handler registration method in workers written in other languages). -/// -/// Since Faktory workers do not all need to be the same (you could have some written in Rust for -/// performance-critical tasks, some in Ruby for more webby tasks, etc.), it may be the case that a -/// given job can only be executed by some workers (e.g., if they job type is not registered at -/// others). To allow for this, Faktory includes a `labels` field with each job. Jobs will only be -/// sent to workers whose labels (see -/// [`ConsumerBuilder::labels`](struct.ConsumerBuilder.html#method.labels)) match those set in -/// `Job::labels`. -/// -/// # Examples -/// -/// Connecting to an unsecured Faktory server using environment variables -/// -/// ```no_run -/// # tokio_test::block_on(async { -/// use faktory::Producer; -/// let p = Producer::connect(None).await.unwrap(); -/// # }); -/// ``` -/// -/// Connecting to a secured Faktory server using an explicit URL -/// -/// ```no_run -/// # tokio_test::block_on(async { -/// use faktory::Producer; -/// let p = Producer::connect(Some("tcp://:hunter2@localhost:7439")).await.unwrap(); -/// # }) -/// ``` -/// -/// Issuing a job using a `Producer` -/// -/// ```no_run -/// # tokio_test::block_on(async { -/// # use faktory::Producer; -/// # let mut p = Producer::connect(None).await.unwrap(); -/// use faktory::Job; -/// p.enqueue(Job::new("foobar", vec!["z"])).await.unwrap(); -/// # }); -/// ``` -/// -pub struct Producer { - c: Client, -} - -impl Producer { - /// Asynchronously enqueue the given job on the Faktory server. - /// - /// Returns `Ok` if the job was successfully queued by the Faktory server. - pub async fn enqueue(&mut self, job: Job) -> Result<(), Error> { - self.c.issue(&Push::from(job)).await?.read_ok().await - } - - /// Enqueue numerous jobs on the Faktory server. - /// - /// Provided you have numerous jobs to submit, using this method will be more efficient as compared - /// to calling [`enqueue`](Producer::enqueue) multiple times. - /// - /// The returned `Ok` result will contain a tuple of enqueued jobs count and an option of a hash map - /// with job ids mapped onto error messages. Therefore `Ok(n, None)` will indicate that all n jobs - /// have been enqueued without errors. - /// - /// Note that this is not an all-or-nothing operation: jobs that contain errors will not be enqueued, - /// while those that are error-free _will_ be enqueued by the Faktory server. - pub async fn enqueue_many( - &mut self, - jobs: J, - ) -> Result<(usize, Option>), Error> - where - J: IntoIterator, - J::IntoIter: ExactSizeIterator, - { - let jobs = jobs.into_iter(); - let jobs_count = jobs.len(); - let errors: HashMap = self - .c - .issue(&PushBulk::from(jobs.collect::>())) - .await? - .read_json() - .await? - .expect("Faktory server sends {} literal when there are no errors"); - if errors.is_empty() { - return Ok((jobs_count, None)); - } - Ok((jobs_count - errors.len(), Some(errors))) - } - - /// Retrieve information about the running server. - /// - /// The returned value is the result of running the `INFO` command on the server. - pub async fn info(&mut self) -> Result { - self.c - .issue(&Info) - .await? - .read_json() - .await - .map(|v| v.expect("info command cannot give empty response")) - } - - /// Pause the given queues. - pub async fn queue_pause(&mut self, queues: &[Q]) -> Result<(), Error> - where - Q: AsRef + Sync, - { - self.c - .issue(&QueueControl::new(QueueAction::Pause, queues)) - .await? - .read_ok() - .await - } - - /// Resume the given queues. - pub async fn queue_resume>(&mut self, queues: &[Q]) -> Result<(), Error> - where - Q: AsRef + Sync, - { - self.c - .issue(&QueueControl::new(QueueAction::Resume, queues)) - .await? - .read_ok() - .await - } - - /// Initiate a new batch of jobs. - #[cfg(feature = "ent")] - #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] - pub async fn start_batch(&mut self, batch: Batch) -> Result, Error> { - let bid = self.c.issue(&batch).await?.read_bid().await?; - Ok(BatchHandle::new(bid, self)) - } - - /// Open an already existing batch of jobs. - /// - /// This will not error if a batch with the provided `bid` does not exist, - /// rather `Ok(None)` will be returned. - #[cfg(feature = "ent")] - #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] - pub async fn open_batch(&mut self, bid: String) -> Result>, Error> { - let bid = self - .c - .issue(&OpenBatch::from(bid)) - .await? - .maybe_bid() - .await?; - Ok(bid.map(|bid| BatchHandle::new(bid, self))) - } - - #[cfg(feature = "ent")] - pub(crate) async fn commit_batch(&mut self, bid: String) -> Result<(), Error> { - self.c.issue(&CommitBatch::from(bid)).await?.read_ok().await - } -} - -impl Producer> { - /// Connect to a Faktory server with a non-standard stream. - pub async fn connect_with( - stream: S, - pwd: Option, - ) -> Result>, Error> { - let buffered = BufStream::new(stream); - let c = Client::connect_with(buffered, pwd).await?; - Ok(Producer { c }) - } -} - -impl Producer> { - /// Create a producer and asynchronously connect to a Faktory server. - /// - /// If `url` is not given, will use the standard Faktory environment variables. Specifically, - /// `FAKTORY_PROVIDER` is read to get the name of the environment variable to get the address - /// from (defaults to `FAKTORY_URL`), and then that environment variable is read to get the - /// server address. If the latter environment variable is not defined, the connection will be - /// made to - /// - /// ```text - /// tcp://localhost:7419 - /// ``` - /// - /// If `url` is given, but does not specify a port, it defaults to 7419. - pub async fn connect(url: Option<&str>) -> Result { - let c = Client::connect(url).await?; - Ok(Producer { c }) - } -} diff --git a/src/proto/batch/mod.rs b/src/proto/batch/mod.rs index 7237c2b6..4e4a2461 100644 --- a/src/proto/batch/mod.rs +++ b/src/proto/batch/mod.rs @@ -1,14 +1,11 @@ -#[cfg(doc)] -use crate::Client; - -use crate::{Error, Job, Producer}; +use crate::{Client, Error, Job}; use chrono::{DateTime, Utc}; use derive_builder::Builder; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; mod cmd; -pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; +pub use cmd::{GetBatchStatus, OpenBatch}; /// Batch of jobs. /// @@ -29,9 +26,9 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// ```no_run /// # tokio_test::block_on(async { /// # use faktory::Error; -/// use faktory::{Producer, Job, ent::Batch}; +/// use faktory::{Client, Job, ent::Batch}; /// -/// let mut prod = Producer::connect(None).await?; +/// let mut cl = Client::connect(None).await?; /// let job1 = Job::builder("job_type").build(); /// let job2 = Job::builder("job_type").build(); /// let job_cb = Job::builder("callback_job_type").build(); @@ -40,7 +37,7 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// .description("Batch description") /// .with_complete_callback(job_cb); /// -/// let mut batch = prod.start_batch(batch).await?; +/// let mut batch = cl.start_batch(batch).await?; /// batch.add(job1).await?; /// batch.add(job2).await?; /// batch.commit().await?; @@ -52,9 +49,9 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// Nested batches are also supported: /// ```no_run /// # tokio_test::block_on(async { -/// # use faktory::{Producer, Job, Error}; +/// # use faktory::{Client, Job, Error}; /// # use faktory::ent::Batch; -/// # let mut prod = Producer::connect(None).await?; +/// # let mut cl = Client::connect(None).await?; /// let parent_job1 = Job::builder("job_type").build(); /// let parent_job2 = Job::builder("another_job_type").build(); /// let parent_cb = Job::builder("callback_job_type").build(); @@ -70,7 +67,7 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// .description("Child batch description") /// .with_success_callback(child_cb); /// -/// let mut parent = prod.start_batch(parent_batch).await?; +/// let mut parent = cl.start_batch(parent_batch).await?; /// parent.add(parent_job1).await?; /// parent.add(parent_job2).await?; /// let mut child = parent.start_batch(child_batch).await?; @@ -91,17 +88,17 @@ pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// You can retieve the batch status using a [`Client`]: /// ```no_run /// # use faktory::Error; -/// # use faktory::{Producer, Job, Client}; +/// # use faktory::{Job, Client}; /// # use faktory::ent::{Batch, CallbackState}; /// # tokio_test::block_on(async { -/// let mut prod = Producer::connect(None).await?; +/// let mut cl = Client::connect(None).await?; /// let job = Job::builder("job_type").build(); /// let cb_job = Job::builder("callback_job_type").build(); /// let b = Batch::builder() /// .description("Batch description") /// .with_complete_callback(cb_job); /// -/// let mut b = prod.start_batch(b).await?; +/// let mut b = cl.start_batch(b).await?; /// let bid = b.id().to_string(); /// b.add(job).await?; /// b.commit().await?; @@ -214,7 +211,7 @@ impl Clone for BatchBuilder { /// Represents a newly started or re-opened batch of jobs. pub struct BatchHandle<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> { bid: String, - prod: &'a mut Producer, + prod: &'a mut Client, } impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { @@ -223,7 +220,7 @@ impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { self.bid.as_ref() } - pub(crate) fn new(bid: String, prod: &mut Producer) -> BatchHandle<'_, S> { + pub(crate) fn new(bid: String, prod: &mut Client) -> BatchHandle<'_, S> { BatchHandle { bid, prod } } @@ -247,7 +244,7 @@ impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { /// /// The Faktory server will not queue any callbacks, unless the batch is committed. /// Committing an empty batch will make the server queue the callback(s) right away. - /// Once committed, the batch can still be re-opened with [open_batch](Producer::open_batch), + /// Once committed, the batch can still be re-opened with [open_batch](Client::open_batch), /// and extra jobs can be added to it. pub async fn commit(self) -> Result<(), Error> { self.prod.commit_batch(self.bid).await @@ -331,10 +328,10 @@ pub struct BatchStatus { impl<'a> BatchStatus { /// Open the batch for which this `BatchStatus` has been retrieved. /// - /// See [`open_batch`](Producer::open_batch). + /// See [`open_batch`](Client::open_batch). pub async fn open( &self, - prod: &'a mut Producer, + prod: &'a mut Client, ) -> Result>, Error> { prod.open_batch(self.bid.clone()).await } diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index fcf13a58..1172dda0 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -1,5 +1,8 @@ -use super::super::{single, BatchStatus, GetBatchStatus, Progress, ProgressUpdate, Track}; +use super::super::{ + single, BatchStatus, GetBatchStatus, OpenBatch, Progress, ProgressUpdate, Track, +}; use super::{Client, ReadToken}; +use crate::ent::{Batch, BatchHandle}; use crate::error::Error; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; @@ -21,6 +24,25 @@ impl Client { let cmd = GetBatchStatus::from(bid); self.issue(&cmd).await?.read_json().await } + + /// Initiate a new batch of jobs. + pub async fn start_batch(&mut self, batch: Batch) -> Result, Error> { + let bid = self.issue(&batch).await?.read_bid().await?; + Ok(BatchHandle::new(bid, self)) + } + + /// Open an already existing batch of jobs. + /// + /// This will not error if a batch with the provided `bid` does not exist, + /// rather `Ok(None)` will be returned. + pub async fn open_batch(&mut self, bid: String) -> Result>, Error> { + let bid = self.issue(&OpenBatch::from(bid)).await?.maybe_bid().await?; + Ok(bid.map(|bid| BatchHandle::new(bid, self))) + } + + pub(crate) async fn commit_batch(&mut self, bid: String) -> Result<(), Error> { + self.issue(&OpenBatch::from(bid)).await?.read_ok().await + } } impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> ReadToken<'a, S> { diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 3a5feb86..bd894fe4 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -1,16 +1,17 @@ -#[cfg(doc)] -use crate::{Consumer, Producer}; - #[cfg(feature = "ent")] #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] mod ent; -use super::utils; -use super::{single, Reconnect}; +#[cfg(doc)] +use crate::proto::{BatchStatus, Progress, ProgressUpdate}; + +use super::{single, Info, Push, QueueAction, QueueControl, Reconnect}; +use super::{utils, PushBulk}; use crate::error::{self, Error}; use crate::Job; -use tokio::io::BufStream; +use std::collections::HashMap; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::io::{AsyncRead, AsyncWrite, BufStream}; use tokio::net::TcpStream as TokioStream; mod options; @@ -185,6 +186,21 @@ pub(crate) enum HeartbeatStatus { Quiet, } +impl Client> { + /// Create new [`Client`] and connect to a Faktory server with a non-standard stream. + pub async fn connect_with( + stream: S, + pwd: Option, + ) -> Result>, Error> { + let buffered = BufStream::new(stream); + let opts = ClientOptions { + password: pwd, + ..Default::default() + }; + Client::new(buffered, opts).await + } +} + impl Client> { /// Create new [`Client`] and connect to a Faktory server. /// @@ -200,8 +216,7 @@ impl Client> { pub async fn connect(url: Option<&str>) -> Result>, Error> { let url = utils::parse_provided_or_from_env(url)?; let stream = TokioStream::connect(utils::host_from_url(&url)).await?; - let buffered = BufStream::new(stream); - Self::connect_with(buffered, url.password().map(|p| p.to_string())).await + Self::connect_with(stream, url.password().map(|p| p.to_string())).await } } @@ -266,15 +281,6 @@ where Ok(c) } - /// Create new [`Client`] and connect to a Faktory server with a non-standard stream. - pub async fn connect_with(stream: S, pwd: Option) -> Result, Error> { - let opts = ClientOptions { - password: pwd, - ..Default::default() - }; - Client::new(stream, opts).await - } - pub(crate) async fn issue( &mut self, c: &FC, @@ -293,6 +299,79 @@ where .await } + /// Asynchronously enqueue the given job on the Faktory server. + /// + /// Returns `Ok` if the job was successfully queued by the Faktory server. + pub async fn enqueue(&mut self, job: Job) -> Result<(), Error> { + self.issue(&Push::from(job)).await?.read_ok().await + } + + /// Enqueue numerous jobs on the Faktory server. + /// + /// Provided you have numerous jobs to submit, using this method will be more efficient as compared + /// to calling [`enqueue`](Client::enqueue) multiple times. + /// + /// The returned `Ok` result will contain a tuple of enqueued jobs count and an option of a hash map + /// with job ids mapped onto error messages. Therefore `Ok(n, None)` will indicate that all n jobs + /// have been enqueued without errors. + /// + /// Note that this is not an all-or-nothing operation: jobs that contain errors will not be enqueued, + /// while those that are error-free _will_ be enqueued by the Faktory server. + pub async fn enqueue_many( + &mut self, + jobs: J, + ) -> Result<(usize, Option>), Error> + where + J: IntoIterator, + J::IntoIter: ExactSizeIterator, + { + let jobs = jobs.into_iter(); + let jobs_count = jobs.len(); + let errors: HashMap = self + .issue(&PushBulk::from(jobs.collect::>())) + .await? + .read_json() + .await? + .expect("Faktory server sends {} literal when there are no errors"); + if errors.is_empty() { + return Ok((jobs_count, None)); + } + Ok((jobs_count - errors.len(), Some(errors))) + } + + /// Retrieve information about the running server. + /// + /// The returned value is the result of running the `INFO` command on the server. + pub async fn info(&mut self) -> Result { + self.issue(&Info) + .await? + .read_json() + .await + .map(|v| v.expect("info command cannot give empty response")) + } + + /// Pause the given queues. + pub async fn queue_pause(&mut self, queues: &[Q]) -> Result<(), Error> + where + Q: AsRef + Sync, + { + self.issue(&QueueControl::new(QueueAction::Pause, queues)) + .await? + .read_ok() + .await + } + + /// Resume the given queues. + pub async fn queue_resume>(&mut self, queues: &[Q]) -> Result<(), Error> + where + Q: AsRef + Sync, + { + self.issue(&QueueControl::new(QueueAction::Resume, queues)) + .await? + .read_ok() + .await + } + pub(crate) async fn heartbeat(&mut self) -> Result { single::write_command( &mut self.stream, diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 3db996dc..daf89f8f 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -19,8 +19,7 @@ pub use self::single::ent::{JobState, Progress, ProgressUpdate, ProgressUpdateBu mod batch; #[cfg(feature = "ent")] pub use batch::{ - Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, CommitBatch, GetBatchStatus, - OpenBatch, + Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, GetBatchStatus, OpenBatch, }; /// A stream that can be re-established after failing. diff --git a/src/tls.rs b/src/tls.rs index 30576140..8ec3c7ff 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -18,10 +18,10 @@ use tokio_rustls::TlsConnector; /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{Producer, TlsStream}; +/// use faktory::{Client, TlsStream}; /// let tls = TlsStream::connect(None).await.unwrap(); -/// let p = Producer::connect_with(tls, None).await.unwrap(); -/// # drop(p); +/// let cl = Client::connect_with(tls, None).await.unwrap(); +/// # drop(cl); /// # }); /// ``` /// diff --git a/tests/producer.rs b/tests/producer.rs index d283da1b..3104c83f 100644 --- a/tests/producer.rs +++ b/tests/producer.rs @@ -10,7 +10,7 @@ use faktory::*; async fn hello() { let mut s = mock::Stream::default(); - let p = Producer::connect_with(s.clone(), None).await.unwrap(); + let p = Client::connect_with(s.clone(), None).await.unwrap(); let written = s.pop_bytes_written(0); eprintln!("{:?}", String::from_utf8(written.clone()).unwrap()); assert!(written.starts_with(b"HELLO {")); @@ -31,7 +31,7 @@ async fn hello() { async fn hello_pwd() { let mut s = mock::Stream::with_salt(1545, "55104dc76695721d"); - let c = Producer::connect_with(s.clone(), Some("foobar".to_string())) + let c = Client::connect_with(s.clone(), Some("foobar".to_string())) .await .unwrap(); let written = s.pop_bytes_written(0); @@ -49,7 +49,7 @@ async fn hello_pwd() { #[tokio::test(flavor = "multi_thread")] async fn enqueue() { let mut s = mock::Stream::default(); - let mut p = Producer::connect_with(s.clone(), None).await.unwrap(); + let mut p = Client::connect_with(s.clone(), None).await.unwrap(); s.ignore(0); s.ok(0); @@ -90,7 +90,7 @@ async fn enqueue() { #[tokio::test(flavor = "multi_thread")] async fn queue_control() { let mut s = mock::Stream::default(); - let mut p = Producer::connect_with(s.clone(), None).await.unwrap(); + let mut p = Client::connect_with(s.clone(), None).await.unwrap(); s.ignore(0); s.ok(0); diff --git a/tests/real/community.rs b/tests/real/community.rs index 8f3248de..736bef1f 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -1,21 +1,21 @@ extern crate faktory; use crate::skip_check; -use faktory::{ConsumerBuilder, Job, JobBuilder, Producer}; +use faktory::{Client, ConsumerBuilder, Job, JobBuilder}; use serde_json::Value; use std::{io, sync}; #[tokio::test(flavor = "multi_thread")] async fn hello_p() { skip_check!(); - let p = Producer::connect(None).await.unwrap(); + let p = Client::connect(None).await.unwrap(); drop(p); } #[tokio::test(flavor = "multi_thread")] async fn enqueue_job() { skip_check!(); - let mut p = Producer::connect(None).await.unwrap(); + let mut p = Client::connect(None).await.unwrap(); p.enqueue(JobBuilder::new("order").build()).await.unwrap(); } @@ -37,7 +37,7 @@ async fn roundtrip() { Ok(()) }); let mut c = c.connect(None).await.unwrap(); - let mut p = Producer::connect(None).await.unwrap(); + let mut p = Client::connect(None).await.unwrap(); p.enqueue( JobBuilder::new("order") .jid(&jid) @@ -74,7 +74,7 @@ async fn multi() { let mut c = c.connect(None).await.unwrap(); - let mut p = Producer::connect(None).await.unwrap(); + let mut p = Client::connect(None).await.unwrap(); p.enqueue(Job::new(local, vec![Value::from(1), Value::from("foo")]).on_queue(local)) .await .unwrap(); @@ -115,7 +115,7 @@ async fn fail() { let mut c = c.connect(None).await.unwrap(); - let mut p = Producer::connect(None).await.unwrap(); + let mut p = Client::connect(None).await.unwrap(); // note that *enqueueing* the jobs didn't fail! p.enqueue(Job::new(local, vec![Value::from(1), Value::from("foo")]).on_queue(local)) @@ -147,7 +147,7 @@ async fn queue() { }); let mut c = c.connect(None).await.unwrap(); - let mut p = Producer::connect(None).await.unwrap(); + let mut p = Client::connect(None).await.unwrap(); p.enqueue(Job::new(local, vec![Value::from(1)]).on_queue(local)) .await .unwrap(); @@ -175,7 +175,7 @@ async fn test_jobs_pushed_in_bulk() { let local_3 = "test_jobs_pushed_in_bulk_3"; let local_4 = "test_jobs_pushed_in_bulk_4"; - let mut p = Producer::connect(None).await.unwrap(); + let mut p = Client::connect(None).await.unwrap(); let (enqueued_count, errors) = p .enqueue_many(vec![ Job::builder("common").queue(local_1).build(), @@ -265,8 +265,8 @@ async fn assert_args_not_empty(j: Job) -> io::Result<()> { async fn test_jobs_created_with_builder() { skip_check!(); - // prepare a producer ("client" in Faktory terms) and consumer ("worker"): - let mut producer = Producer::connect(None).await.unwrap(); + // prepare a client and a worker: + let mut client = Client::connect(None).await.unwrap(); let mut consumer = ConsumerBuilder::default(); consumer.register("rebuild_index", assert_args_empty); consumer.register("register_order", assert_args_not_empty); @@ -287,9 +287,9 @@ async fn test_jobs_created_with_builder() { job3.queue = "test_jobs_created_with_builder_1".to_string(); // enqueue ... - producer.enqueue(job1).await.unwrap(); - producer.enqueue(job2).await.unwrap(); - producer.enqueue(job3).await.unwrap(); + client.enqueue(job1).await.unwrap(); + client.enqueue(job2).await.unwrap(); + client.enqueue(job3).await.unwrap(); // ... and execute: let had_job = consumer diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index ef2ca46b..5d1e104b 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -42,8 +42,8 @@ async fn ent_expiring_job() { let url = learn_faktory_url(); let local = "ent_expiring_job"; - // prepare a producer ("client" in Faktory terms) and consumer ("worker"): - let mut p = Producer::connect(Some(&url)).await.unwrap(); + // prepare a client and a worker: + let mut p = Client::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); c.register("AnExpiringJob", print_job); let mut c = c.connect(Some(&url)).await.unwrap(); @@ -92,7 +92,7 @@ async fn ent_unique_job() { let job_type = "order"; // prepare producer and consumer: - let mut p = Producer::connect(Some(&url)).await.unwrap(); + let mut p = Client::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); c.register(job_type, print_job); let mut c = c.connect(Some(&url)).await.unwrap(); @@ -208,7 +208,7 @@ async fn ent_unique_job_until_success() { // send a job difficulty level as a job's args and the lattter // will sleep for a corresponding period of time, pretending // to work hard: - let mut producer_a = Producer::connect(Some(&url1)).await.unwrap(); + let mut producer_a = Client::connect(Some(&url1)).await.unwrap(); let mut consumer_a = ConsumerBuilder::default_async(); consumer_a.register(job_type, |job| async move { let args = job.args().to_owned(); @@ -239,7 +239,7 @@ async fn ent_unique_job_until_success() { time::sleep(time::Duration::from_secs(1)).await; // continue - let mut producer_b = Producer::connect(Some(&url)).await.unwrap(); + let mut producer_b = Client::connect(Some(&url)).await.unwrap(); // this one is a 'duplicate' because the job is still // being executed in the spawned thread: @@ -288,7 +288,7 @@ async fn ent_unique_job_until_start() { let url1 = url.clone(); let handle = tokio::spawn(async move { - let mut producer_a = Producer::connect(Some(&url1)).await.unwrap(); + let mut producer_a = Client::connect(Some(&url1)).await.unwrap(); let mut consumer_a = ConsumerBuilder::default_async(); consumer_a.register(job_type, |job| async move { let args = job.args().to_owned(); @@ -324,7 +324,7 @@ async fn ent_unique_job_until_start() { time::sleep(time::Duration::from_secs(1)).await; // the unique lock has been released by this time, so the job is enqueued successfully: - let mut producer_b = Producer::connect(Some(&url)).await.unwrap(); + let mut producer_b = Client::connect(Some(&url)).await.unwrap(); producer_b .enqueue( JobBuilder::new(job_type) @@ -346,7 +346,7 @@ async fn ent_unique_job_bypass_unique_lock() { skip_if_not_enterprise!(); let url = learn_faktory_url(); - let mut producer = Producer::connect(Some(&url)).await.unwrap(); + let mut producer = Client::connect(Some(&url)).await.unwrap(); let queue_name = "ent_unique_job_bypass_unique_lock"; let job1 = Job::builder("order") .queue(queue_name) @@ -405,7 +405,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { .expect("job progress tracker created successfully"), )); - let mut p = Producer::connect(Some(&url)).await.unwrap(); + let mut p = Client::connect(Some(&url)).await.unwrap(); let job_tackable = JobBuilder::new("order") .args(vec![Value::from("ISBN-13:9781718501850")]) @@ -558,7 +558,7 @@ async fn test_batch_of_jobs_can_be_initiated() { skip_if_not_enterprise!(); let url = learn_faktory_url(); - let mut p = Producer::connect(Some(&url)).await.unwrap(); + let mut p = Client::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); c.register("thumbnail", |_job| async { Ok::<(), io::Error>(()) }); c.register("clean_up", |_job| async { Ok(()) }); @@ -695,7 +695,7 @@ async fn test_batches_can_be_nested() { let url = learn_faktory_url(); // Set up 'producer', 'consumer', and 'tracker': - let mut p = Producer::connect(Some(&url)).await.unwrap(); + let mut p = Client::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); c.register("jobtype", |_job| async { Ok::<(), io::Error>(()) }); let mut _c = c.connect(Some(&url)).await.unwrap(); @@ -795,7 +795,7 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { let url = learn_faktory_url(); // prepare a producer, a consumer of 'order' jobs, and a tracker: - let mut p = Producer::connect(Some(&url)).await.unwrap(); + let mut p = Client::connect(Some(&url)).await.unwrap(); let mut c = ConsumerBuilder::default(); c.register("order", |_job| async { Ok(()) }); c.register("order_clean_up", |_job| async { Ok::<(), io::Error>(()) }); @@ -886,7 +886,7 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { skip_if_not_enterprise!(); let url = learn_faktory_url(); - let mut p = Producer::connect(Some(&url)).await.unwrap(); + let mut p = Client::connect(Some(&url)).await.unwrap(); let mut t = Client::connect(Some(&url)).await.unwrap(); let q_name = "test_callback_will_be_queued_upon_commit_even_if_batch_is_empty"; let complete_cb_jobtype = "complete_callback_jobtype"; @@ -959,7 +959,7 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { async fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { skip_if_not_enterprise!(); let url = learn_faktory_url(); - let mut p = Producer::connect(Some(&url)).await.unwrap(); + let mut p = Client::connect(Some(&url)).await.unwrap(); let mut t = Client::connect(Some(&url)).await.unwrap(); let mut jobs = some_jobs("order", "test_batch_can_be_reopned_add_extra_jobs_added", 4); let mut callbacks = some_jobs( diff --git a/tests/tls.rs b/tests/tls.rs index 893283d3..56c4ded4 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -55,7 +55,7 @@ async fn roundtrip_tls() { }; let mut c = c.connect_with(tls().await, None).await.unwrap(); - let mut p = Producer::connect_with(tls().await, None).await.unwrap(); + let mut p = Client::connect_with(tls().await, None).await.unwrap(); p.enqueue(Job::new(local, vec!["z"]).on_queue(local)) .await .unwrap(); From 6d11ad9a305b237519c100a21e6d52d2ac2b3e75 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sun, 25 Feb 2024 19:26:14 +0500 Subject: [PATCH 013/129] Fix 'commit_batch' on Client --- src/proto/batch/mod.rs | 14 +++++++------- src/proto/client/ent.rs | 4 ++-- src/proto/mod.rs | 3 ++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/proto/batch/mod.rs b/src/proto/batch/mod.rs index 4e4a2461..ffc9c7ba 100644 --- a/src/proto/batch/mod.rs +++ b/src/proto/batch/mod.rs @@ -5,7 +5,7 @@ use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; mod cmd; -pub use cmd::{GetBatchStatus, OpenBatch}; +pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// Batch of jobs. /// @@ -211,7 +211,7 @@ impl Clone for BatchBuilder { /// Represents a newly started or re-opened batch of jobs. pub struct BatchHandle<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> { bid: String, - prod: &'a mut Client, + c: &'a mut Client, } impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { @@ -220,8 +220,8 @@ impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { self.bid.as_ref() } - pub(crate) fn new(bid: String, prod: &mut Client) -> BatchHandle<'_, S> { - BatchHandle { bid, prod } + pub(crate) fn new(bid: String, c: &mut Client) -> BatchHandle<'_, S> { + BatchHandle { bid, c } } /// Add the given job to the batch. @@ -231,13 +231,13 @@ impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { /// returned as `Some()`. pub async fn add(&mut self, mut job: Job) -> Result, Error> { let bid = job.custom.insert("bid".into(), self.bid.clone().into()); - self.prod.enqueue(job).await.map(|_| bid) + self.c.enqueue(job).await.map(|_| bid) } /// Initiate a child batch of jobs. pub async fn start_batch(&mut self, mut batch: Batch) -> Result, Error> { batch.parent_bid = Some(self.bid.clone()); - self.prod.start_batch(batch).await + self.c.start_batch(batch).await } /// Commit this batch. @@ -247,7 +247,7 @@ impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { /// Once committed, the batch can still be re-opened with [open_batch](Client::open_batch), /// and extra jobs can be added to it. pub async fn commit(self) -> Result<(), Error> { - self.prod.commit_batch(self.bid).await + self.c.commit_batch(self.bid).await } } diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index 1172dda0..cc252a3a 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -1,5 +1,5 @@ use super::super::{ - single, BatchStatus, GetBatchStatus, OpenBatch, Progress, ProgressUpdate, Track, + single, BatchStatus, CommitBatch, GetBatchStatus, OpenBatch, Progress, ProgressUpdate, Track, }; use super::{Client, ReadToken}; use crate::ent::{Batch, BatchHandle}; @@ -41,7 +41,7 @@ impl Client { } pub(crate) async fn commit_batch(&mut self, bid: String) -> Result<(), Error> { - self.issue(&OpenBatch::from(bid)).await?.read_ok().await + self.issue(&CommitBatch::from(bid)).await?.read_ok().await } } diff --git a/src/proto/mod.rs b/src/proto/mod.rs index daf89f8f..3db996dc 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -19,7 +19,8 @@ pub use self::single::ent::{JobState, Progress, ProgressUpdate, ProgressUpdateBu mod batch; #[cfg(feature = "ent")] pub use batch::{ - Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, GetBatchStatus, OpenBatch, + Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, CommitBatch, GetBatchStatus, + OpenBatch, }; /// A stream that can be re-established after failing. From a875a3577488254e4fa4a1cdc6e241bd0bcd826a Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sun, 25 Feb 2024 20:07:31 +0500 Subject: [PATCH 014/129] Consumer -> Worker --- README.md | 42 +++++----- src/bin/loadtest.rs | 6 +- src/lib.rs | 26 +++--- src/proto/client/mod.rs | 4 +- src/proto/client/options.rs | 2 +- src/tls.rs | 5 +- src/{consumer => worker}/builder.rs | 46 +++++------ src/{consumer => worker}/health.rs | 4 +- src/{consumer => worker}/mod.rs | 72 ++++++++-------- src/{consumer => worker}/registries.rs | 0 src/{consumer => worker}/runner.rs | 17 ++-- tests/consumer.rs | 16 ++-- tests/real/community.rs | 34 ++++---- tests/real/enterprise.rs | 110 ++++++++++++++----------- tests/tls.rs | 2 +- 15 files changed, 201 insertions(+), 185 deletions(-) rename src/{consumer => worker}/builder.rs (71%) rename src/{consumer => worker}/health.rs (96%) rename src/{consumer => worker}/mod.rs (85%) rename src/{consumer => worker}/registries.rs (100%) rename src/{consumer => worker}/runner.rs (86%) diff --git a/README.md b/README.md index bd55d9e4..c5eae769 100644 --- a/README.md +++ b/README.md @@ -19,60 +19,64 @@ jobs. A client enqueues a job, Faktory sends the job to an available worker (and they're all busy), the worker executes the job, and eventually reports back to Faktory that the job has completed. -Jobs are self-contained, and consist of a job *type* (a string), arguments for the job, and +Jobs are self-contained, and consist of a job _type_ (a string), arguments for the job, and bits and pieces of metadata. When a job is scheduled for execution, the worker is given this information, and uses the job type to figure out how to execute the job. You can think of job execution as a remote function call (or RPC) where the job type is the name of the function, and the job arguments are, perhaps unsuprisingly, the arguments to the function. -In this crate, you will find bindings both for submitting jobs (clients that *produce* jobs) -and for executing jobs (workers that *consume* jobs). The former can be done by making a -`Producer`, whereas the latter is done with a `Consumer`. See the documentation for each for +In this crate, you will find bindings both for submitting jobs (clients that _produce_ jobs) +and for executing jobs (workers that _consume_ jobs). The former can be done by making a +`Client`, whereas the latter is done with a `Worker`. See the documentation for each for more details on how to use them. ## Encrypted connections (TLS) To connect to a Faktory server hosted over TLS, add the `tls` feature, and see the -documentation for `TlsStream`, which can be supplied to `Producer::connect_with` and -`Consumer::connect_with`. +documentation for `TlsStream`, which can be supplied to `Client::connect_with` and +`WorkerBuilder::connect_with`. ## Examples -If you want to **submit** jobs to Faktory, use `Producer`. +If you want to **submit** jobs to Faktory, use `Client`. ```rust -use faktory::{Producer, Job}; -let mut p = Producer::connect(None).unwrap(); -p.enqueue(Job::new("foobar", vec!["z"])).unwrap(); +use faktory::{Client, Job}; +let mut c = Client::connect(None).await.unwrap(); +c.enqueue(Job::new("foobar", vec!["z"])).await.unwrap(); ``` -If you want to **accept** jobs from Faktory, use `Consumer`. +If you want to **accept** jobs from Faktory, use `Worker`. ```rust -use faktory::ConsumerBuilder; +use faktory::WorkerBuilder; use std::io; -let mut c = ConsumerBuilder::default(); -c.register("foobar", |job| -> io::Result<()> { - println!("{:?}", job); - Ok(()) +let mut w = WorkerBuilder::default(); +w.register("foobar", |job| async move { + println!("{:?}", job); + Ok::<(), io::Error>(()) }); -let mut c = c.connect(None).unwrap(); -if let Err(e) = c.run(&["default"]) { +let mut w = w.connect(None).await.unwrap(); +if let Err(e) = w.run(&["default"]).await { println!("worker failed: {}", e); } ``` ## Run test suite locally -First ensure the "Factory" service is running and accepting connections on your machine. +First ensure the "Factory" service is running and accepting connections on your machine. To launch it a [Factory](https://hub.docker.com/r/contribsys/faktory/) container with [docker](https://docs.docker.com/engine/install/), run: + ```bash docker run --rm -it -v faktory-data:/var/lib/faktory -p 127.0.0.1:7419:7419 -p 127.0.0.1:7420:7420 contribsys/faktory:latest /faktory -b :7419 -w :7420 ``` + After that run the tests: + ```bash FAKTORY_URL=tcp://127.0.0.1:7419 cargo test --all-features --locked --all-targets ``` + Please note that setting "FAKTORY_URL" environment variable is required for e2e tests to not be skipped. Provided you have [make](https://www.gnu.org/software/make/#download) installed and `docker` daemon running, diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index 94adc72a..095912ba 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -13,7 +13,7 @@ const QUEUES: &[&str] = &["queue0", "queue1", "queue2", "queue3", "queue4"]; async fn main() { let matches = Command::new("My Super Program (Async)") .version("0.1") - .about("Benchmark the performance of Rust Faktory async consumers and producers") + .about("Benchmark the performance of Rust Faktory async workers and client") .arg( Arg::new("jobs") .help("Number of jobs to run") @@ -23,7 +23,7 @@ async fn main() { ) .arg( Arg::new("threads") - .help("Number of consumers/producers to run") + .help("Number of workers/clients to run") .value_parser(value_parser!(usize)) .index(2) .default_value("10"), @@ -56,7 +56,7 @@ async fn main() { tokio::spawn(async move { // make producer and consumer let mut p = Client::connect(None).await.unwrap(); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.register("SomeJob", |_| { Box::pin(async move { let mut rng = rand::thread_rng(); diff --git a/src/lib.rs b/src/lib.rs index 9858cea6..9d41334e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,18 +20,18 @@ //! //! In this crate, you will find bindings both for submitting jobs (clients that *produce* jobs) //! and for executing jobs (workers that *consume* jobs). The former can be done by making a -//! `Producer`, whereas the latter is done with a `Consumer`. See the documentation for each for +//! `Client`, whereas the latter is done with a `Worker`. See the documentation for each for //! more details on how to use them. //! //! # Encrypted connections (TLS) //! //! To connect to a Faktory server hosted over TLS, add the `tls` feature, and see the -//! documentation for `TlsStream`, which can be supplied to `Producer::connect_with` and -//! `Consumer::connect_with`. +//! documentation for `TlsStream`, which can be supplied to [`Client::connect_with`] and +//! [`WorkerBuilder::connect_with`]. //! //! # Examples //! -//! If you want to **submit** jobs to Faktory, use `Producer`. +//! If you want to **submit** jobs to Faktory, use `Client`. //! //! ```no_run //! # tokio_test::block_on(async { @@ -44,19 +44,19 @@ //! assert_eq!(errors, None); //! }); //! ``` -//! If you want to **accept** jobs from Faktory, use `Consumer`. +//! If you want to **accept** jobs from Faktory, use `Worker`. //! //! ```no_run //! # tokio_test::block_on(async { -//! use faktory::ConsumerBuilder; +//! use faktory::WorkerBuilder; //! use std::io; -//! let mut c = ConsumerBuilder::default(); -//! c.register("foobar", |job| Box::pin(async move { +//! let mut w = WorkerBuilder::default(); +//! w.register("foobar", |job| async move { //! println!("{:?}", job); //! Ok::<(), io::Error>(()) -//! })); -//! let mut c = c.connect(None).await.unwrap(); -//! if let Err(e) = c.run(&["default"]).await { +//! }); +//! let mut w = w.connect(None).await.unwrap(); +//! if let Err(e) = w.run(&["default"]).await { //! println!("worker failed: {}", e); //! } //! # }); @@ -70,12 +70,12 @@ extern crate serde_derive; pub mod error; -mod consumer; mod proto; +mod worker; -pub use crate::consumer::{Consumer, ConsumerBuilder, JobRunner}; pub use crate::error::Error; pub use crate::proto::{Client, Job, JobBuilder, Reconnect}; +pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; #[cfg(feature = "ent")] #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index bd894fe4..b11eb214 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -52,7 +52,7 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// [`Client::enqueue`](Client::enqueue). The most important part of a `Job` /// is its `kind`; this field dictates how workers will execute the job when they receive it. The /// string provided here must match a handler registered on the worker using -/// [`ConsumerBuilder::register`](struct.ConsumerBuilder.html#method.register) (or the equivalent +/// [`WorkerBuilder::register`](struct.WorkerBuilder.html#method.register) (or the equivalent /// handler registration method in workers written in other languages). /// /// Since Faktory workers do not all need to be the same (you could have some written in Rust for @@ -60,7 +60,7 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// given job can only be executed by some workers (e.g., if they job type is not registered at /// others). To allow for this, Faktory includes a `labels` field with each job. Jobs will only be /// sent to workers whose labels (see -/// [`ConsumerBuilder::labels`](struct.ConsumerBuilder.html#method.labels)) match those set in +/// [`WorkerBuilder::labels`](struct.WorkerBuilder.html#method.labels)) match those set in /// `Job::labels`. /// /// # Examples diff --git a/src/proto/client/options.rs b/src/proto/client/options.rs index 67dc0a2a..113cf48e 100644 --- a/src/proto/client/options.rs +++ b/src/proto/client/options.rs @@ -21,7 +21,7 @@ pub(crate) struct ClientOptions { pub(crate) password: Option, /// Whether this client is instatianted for - /// a consumer ("worker" in Faktory terms). + /// a worker (i.e. to consume jobs). pub(crate) is_worker: bool, } diff --git a/src/tls.rs b/src/tls.rs index 8ec3c7ff..b6126df7 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -1,3 +1,6 @@ +#[cfg(doc)] +use crate::{Client, WorkerBuilder}; + use crate::{proto::utils, Error, Reconnect}; use std::fmt::Debug; use std::io; @@ -11,7 +14,7 @@ use tokio_rustls::TlsConnector; /// A reconnectable asynchronous stream encrypted with TLS. /// -/// This can be used as an argument to `Consumer::connect_with` and `Producer::connect_with` to +/// This can be used as an argument to [`WorkerBuilder::connect_with`] and [`Client::connect_with`] to /// connect to a TLS-secured Faktory server. /// /// # Examples diff --git a/src/consumer/builder.rs b/src/worker/builder.rs similarity index 71% rename from src/consumer/builder.rs rename to src/worker/builder.rs index 9bbcbf9e..eb417ec7 100644 --- a/src/consumer/builder.rs +++ b/src/worker/builder.rs @@ -1,9 +1,6 @@ -use super::{runner::Closure, CallbacksRegistry, Client, Consumer}; +use super::{runner::Closure, CallbacksRegistry, Client, Worker}; use crate::{ - proto::{ - utils::{get_env_url, host_from_url, url_parse}, - ClientOptions, - }, + proto::{utils, ClientOptions}, Error, Job, JobRunner, }; use std::future::Future; @@ -12,26 +9,26 @@ use tokio::net::TcpStream as TokioStream; /// Convenience wrapper for building a Faktory worker. /// -/// See the [`Consumer`] documentation for details. -pub struct ConsumerBuilder { +/// See the [`Worker`] documentation for details. +pub struct WorkerBuilder { opts: ClientOptions, workers_count: usize, callbacks: CallbacksRegistry, } -impl ConsumerBuilder { - /// Create a builder for asynchronous version of `Consumer`. - pub fn default_async() -> ConsumerBuilder { - ConsumerBuilder::default() +impl WorkerBuilder { + /// Create a builder for asynchronous version of `Worker`. + pub fn default_async() -> WorkerBuilder { + WorkerBuilder::default() } } -impl Default for ConsumerBuilder { - /// Create a builder for asynchronous version of `Consumer`. +impl Default for WorkerBuilder { + /// Create a builder for asynchronous version of `ConWorkersumer`. /// - /// See [`ConsumerBuilder`](struct.ConsumerBuilder.html) + /// See [`WorkerBuilder`](struct.WorkerBuilder.html) fn default() -> Self { - ConsumerBuilder { + WorkerBuilder { opts: ClientOptions::default(), workers_count: 1, callbacks: CallbacksRegistry::default(), @@ -39,7 +36,7 @@ impl Default for ConsumerBuilder { } } -impl ConsumerBuilder { +impl WorkerBuilder { /// Set the hostname to use for this worker. /// /// Defaults to the machine's hostname as reported by the operating system. @@ -105,28 +102,25 @@ impl ConsumerBuilder { mut self, stream: S, pwd: Option, - ) -> Result, E>, Error> { + ) -> Result, E>, Error> { self.opts.password = pwd; self.opts.is_worker = true; let buffered = BufStream::new(stream); let client = Client::new(buffered, self.opts).await?; - Ok(Consumer::new(client, self.workers_count, self.callbacks).await) + Ok(Worker::new(client, self.workers_count, self.callbacks).await) } /// Asynchronously connect to a Faktory server. /// - /// See [`connect`](struct.ConsumerBuilder.html#structmethod.connect). + /// See [`connect`](WorkerBuilder::connect). pub async fn connect( self, url: Option<&str>, - ) -> Result, E>, Error> { - let url = match url { - Some(url) => url_parse(url), - None => url_parse(&get_env_url()), - }?; - let stream = TokioStream::connect(host_from_url(&url)).await?; + ) -> Result, E>, Error> { + let url = utils::parse_provided_or_from_env(url)?; + let stream = TokioStream::connect(utils::host_from_url(&url)).await?; let buffered = BufStream::new(stream); let client = Client::new(buffered, self.opts).await?; - Ok(Consumer::new(client, self.workers_count, self.callbacks).await) + Ok(Worker::new(client, self.workers_count, self.callbacks).await) } } diff --git a/src/consumer/health.rs b/src/worker/health.rs similarity index 96% rename from src/consumer/health.rs rename to src/worker/health.rs index 50ff2def..a9f98da1 100644 --- a/src/consumer/health.rs +++ b/src/worker/health.rs @@ -1,4 +1,4 @@ -use super::{Consumer, STATUS_QUIET, STATUS_RUNNING, STATUS_TERMINATING}; +use super::{Worker, STATUS_QUIET, STATUS_RUNNING, STATUS_TERMINATING}; use crate::{proto::HeartbeatStatus, Error, Reconnect}; use std::{ error::Error as StdError, @@ -11,7 +11,7 @@ use tokio::time::sleep as tokio_sleep; impl< S: AsyncBufReadExt + AsyncWriteExt + Reconnect + Send + Unpin + 'static, E: StdError + 'static + Send, - > Consumer + > Worker { pub(crate) async fn listen_for_heartbeats( &mut self, diff --git a/src/consumer/mod.rs b/src/worker/mod.rs similarity index 85% rename from src/consumer/mod.rs rename to src/worker/mod.rs index 62bc550f..8d5449aa 100644 --- a/src/consumer/mod.rs +++ b/src/worker/mod.rs @@ -13,7 +13,7 @@ mod health; mod registries; mod runner; -pub use builder::ConsumerBuilder; +pub use builder::WorkerBuilder; use registries::{CallbacksRegistry, StatesRegistry}; pub use runner::JobRunner; @@ -21,21 +21,21 @@ pub(crate) const STATUS_RUNNING: usize = 0; pub(crate) const STATUS_QUIET: usize = 1; pub(crate) const STATUS_TERMINATING: usize = 2; -/// `Consumer` is used to run a worker that processes jobs provided by Faktory. +/// `Worker` is used to run a worker that processes jobs provided by Faktory. /// /// # Building the worker /// /// Faktory needs a decent amount of information from its workers, such as a unique worker ID, a /// hostname for the worker, its process ID, and a set of labels used to identify the worker. In /// order to enable setting all these, constructing a worker is a two-step process. You first use a -/// [`ConsumerBuilder`](struct.ConsumerBuilder.html) (which conveniently implements a sensible +/// [`WorkerBuilder`] (which conveniently implements a sensible /// `Default`) to set the worker metadata, as well as to register any job handlers. You then use /// one of the `connect_*` methods to finalize the worker and connect to the Faktory server. /// -/// In most cases, `ConsumerBuilder::default()` will do what you want. You only need to augment it -/// with calls to [`register`](struct.ConsumerBuilder.html#method.register) to register handlers +/// In most cases, [`WorkerBuilder::default()`] will do what you want. You only need to augment it +/// with calls to [`register`](WorkerBuilder::register) to register handlers /// for each of your job types, and then you can connect. If you have different *types* of workers, -/// you may also want to use [`labels`](struct.ConsumerBuilder.html#method.labels) to distinguish +/// you may also want to use [`labels`](WorkerBuilder::labels) to distinguish /// them in the Faktory Web UI. To specify that some jobs should only go to some workers, use /// different queues. /// @@ -63,14 +63,14 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// use the last approach and let the library handle the concurrency for you. /// /// - You can spin up more worker processes by launching your worker program more than once. -/// - You can create more than one `Consumer`. -/// - You can call [`ConsumerBuilder::workers`](struct.ConsumerBuilder.html#method.workers) to set -/// the number of worker threads you'd like the `Consumer` to use internally. +/// - You can create more than one `Worker`. +/// - You can call [`WorkerBuilder::workers`] to set +/// the number of worker threads you'd like the `Worker` to use internally. /// /// # Connecting to Faktory /// -/// To fetch jobs, the `Consumer` must first be connected to the Faktory server. Exactly how you do -/// that depends on your setup. In most cases, you'll want to use `Consumer::connect`, and provide +/// To fetch jobs, the `Worker` must first be connected to the Faktory server. Exactly how you do +/// that depends on your setup. In most cases, you'll want to use [`WorkerBuilder::connect`], and provide /// a connection URL. If you supply a URL, it must be of the form: /// /// ```text @@ -81,7 +81,7 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// their docs for more information) with `localhost:7419` as the fallback default. If you want /// this behavior, pass `None` as the URL. /// -/// See the [`Producer` examples](struct.Producer.html#examples) for examples of how to connect to +/// See the [`Client` examples](struct.Client.html#examples) for examples of how to connect to /// different Factory setups. /// /// # Worker lifecycle @@ -90,9 +90,9 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// /// If all this process is doing is handling jobs, reconnecting on failure, and exiting when told /// to by the Faktory server, you should use -/// [`run_to_completion`](Consumer::run_to_completion). If you want more +/// [`run_to_completion`](Worker::run_to_completion). If you want more /// fine-grained control over the lifetime of your process, you should use -/// [`run`](Consumer::run). See the documentation for each of these +/// [`run`](Worker::run). See the documentation for each of these /// methods for details. /// /// # Examples @@ -102,7 +102,7 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{ConsumerBuilder, Job}; +/// use faktory::{WorkerBuilder, Job}; /// use std::io; /// /// async fn process_job(job: Job) -> io::Result<()> { @@ -110,12 +110,12 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// Ok(()) /// } /// -/// let mut c = ConsumerBuilder::default(); +/// let mut w = WorkerBuilder::default(); /// -/// c.register("foo", process_job); +/// w.register("foo", process_job); /// -/// let mut c = c.connect(None).await.unwrap(); -/// if let Err(e) = c.run(&["default"]).await { +/// let mut w = w.connect(None).await.unwrap(); +/// if let Err(e) = w.run(&["default"]).await { /// println!("worker failed: {}", e); /// } /// # }); @@ -124,34 +124,34 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// Handler can be inlined. /// /// ```no_run -/// # use faktory::ConsumerBuilder; +/// # use faktory::WorkerBuilder; /// # use std::io; -/// let mut c = ConsumerBuilder::default(); -/// c.register("bar", |job| async move { +/// let mut w = WorkerBuilder::default(); +/// w.register("bar", |job| async move { /// println!("{:?}", job); /// Ok::<(), io::Error>(()) /// }); /// ``` /// /// You can also register anything that implements [`JobRunner`] to handle jobs -/// with [`register_runner`](ConsumerBuilder::register_runner). +/// with [`register_runner`](WorkerBuilder::register_runner). /// -pub struct Consumer { +pub struct Worker { c: Client, worker_states: Arc, callbacks: Arc>, terminated: bool, } -impl Consumer { +impl Worker { async fn reconnect(&mut self) -> Result<(), Error> { self.c.reconnect().await } } -impl Consumer { +impl Worker { async fn new(c: Client, workers_count: usize, callbacks: CallbacksRegistry) -> Self { - Consumer { + Worker { c, callbacks: Arc::new(callbacks), worker_states: Arc::new(StatesRegistry::new(workers_count)), @@ -165,9 +165,7 @@ enum Failed { BadJobType(String), } -impl - Consumer -{ +impl Worker { async fn run_job(&mut self, job: Job) -> Result<(), Failed> { let handler = self .callbacks @@ -186,7 +184,7 @@ impl Result<(), Error> { let worker_states = Arc::get_mut(&mut self.worker_states) - .expect("all workers are scoped to &mut of the user-code-visible Consumer"); + .expect("all workers are scoped to &mut of the user-code-visible Worker"); // retry delivering notification about our last job result. // we know there's no leftover thread at this point, so there's no race on the option. @@ -281,10 +279,10 @@ impl Consumer + > Worker { async fn for_worker(&mut self) -> Result { - Ok(Consumer { + Ok(Worker { c: self.c.connect_again().await?, callbacks: Arc::clone(&self.callbacks), worker_states: Arc::clone(&self.worker_states), @@ -320,11 +318,9 @@ impl< /// /// The value in an `Ok` indicates the number of workers that may still be processing jobs. /// - /// Note that if the worker fails, [`reconnect()`](struct.Consumer.html#method.reconnect) - /// should likely be called before calling `run()` again. If an error occurred while reporting - /// a job success or failure, the result will be re-reported to the server without re-executing - /// the job. If the worker was terminated (i.e., `run` returns with an `Ok` response), the - /// worker should **not** try to resume by calling `run` again. This will cause a panic. + /// If an error occurred while reporting a job success or failure, the result will be re-reported to the server + /// without re-executing the job. If the worker was terminated (i.e., `run` returns with an `Ok` response), + /// the worker should **not** try to resume by calling `run` again. This will cause a panic. pub async fn run(&mut self, queues: &[Q]) -> Result where Q: AsRef, diff --git a/src/consumer/registries.rs b/src/worker/registries.rs similarity index 100% rename from src/consumer/registries.rs rename to src/worker/registries.rs diff --git a/src/consumer/runner.rs b/src/worker/runner.rs similarity index 86% rename from src/consumer/runner.rs rename to src/worker/runner.rs index 0d6e7aa6..71a6545c 100644 --- a/src/consumer/runner.rs +++ b/src/worker/runner.rs @@ -1,17 +1,20 @@ use crate::Job; use std::future::Future; -/// Implementations of this trait can be registered to run jobs in a `Consumer`. +#[cfg(doc)] +use super::Worker; + +/// Implementations of this trait can be registered to run jobs in a [`Worker`](Worker). /// /// # Example /// /// Create a worker with all default options, register a single handler (for the `foo` job /// type), connect to the Faktory server, and start accepting jobs. -/// The handler is a struct that implements `JobRunner`. +/// The handler is a struct that implements [`JobRunner`]. /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{ConsumerBuilder, JobRunner, Job}; +/// use faktory::{WorkerBuilder, JobRunner, Job}; /// use std::io; /// /// struct MyHandler { @@ -28,13 +31,13 @@ use std::future::Future; /// } /// } /// -/// let mut c = ConsumerBuilder::default(); +/// let mut w = WorkerBuilder::default(); /// let handler = MyHandler { /// config: "bar".to_string(), /// }; -/// c.register_runner("foo", handler); -/// let mut c = c.connect(None).await.unwrap(); -/// if let Err(e) = c.run(&["default"]).await { +/// w.register_runner("foo", handler); +/// let mut w = w.connect(None).await.unwrap(); +/// if let Err(e) = w.run(&["default"]).await { /// println!("worker failed: {}", e); /// } /// }); diff --git a/tests/consumer.rs b/tests/consumer.rs index 1ddca0c6..d615c841 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -11,7 +11,7 @@ use tokio::{spawn, time::sleep}; #[tokio::test(flavor = "multi_thread")] async fn hello() { let mut s = mock::Stream::default(); - let mut c: ConsumerBuilder = ConsumerBuilder::default(); + let mut c: WorkerBuilder = WorkerBuilder::default(); c.hostname("host".to_string()) .wid("wid".to_string()) .labels(vec!["foo".to_string(), "bar".to_string()]); @@ -40,7 +40,7 @@ async fn hello() { async fn hello_pwd() { let mut s = mock::Stream::with_salt(1545, "55104dc76695721d"); - let mut c: ConsumerBuilder = ConsumerBuilder::default(); + let mut c: WorkerBuilder = WorkerBuilder::default(); c.register("never_called", |_j: Job| async move { unreachable!() }); let c = c .connect_with(s.clone(), Some("foobar".to_string())) @@ -61,7 +61,7 @@ async fn hello_pwd() { #[tokio::test(flavor = "multi_thread")] async fn dequeue() { let mut s = mock::Stream::default(); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.register("foobar", |job: Job| async move { assert_eq!(job.args(), &["z"]); Ok::<(), io::Error>(()) @@ -100,7 +100,7 @@ async fn dequeue() { #[tokio::test(flavor = "multi_thread")] async fn dequeue_first_empty() { let mut s = mock::Stream::default(); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.register("foobar", |job: Job| async move { assert_eq!(job.args(), &["z"]); Ok::<(), io::Error>(()) @@ -155,7 +155,7 @@ async fn dequeue_first_empty() { #[tokio::test(flavor = "multi_thread")] async fn well_behaved() { let mut s = mock::Stream::new(2); // main plus worker - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.wid("wid".to_string()); c.register("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second @@ -220,7 +220,7 @@ async fn well_behaved() { #[tokio::test(flavor = "multi_thread")] async fn no_first_job() { let mut s = mock::Stream::new(2); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.wid("wid".to_string()); c.register("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second @@ -286,7 +286,7 @@ async fn no_first_job() { #[tokio::test(flavor = "multi_thread")] async fn well_behaved_many() { let mut s = mock::Stream::new(3); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.workers(2); c.wid("wid".to_string()); c.register("foobar", |_| async move { @@ -362,7 +362,7 @@ async fn well_behaved_many() { #[tokio::test(flavor = "multi_thread")] async fn terminate() { let mut s = mock::Stream::new(2); - let mut c: ConsumerBuilder = ConsumerBuilder::default(); + let mut c: WorkerBuilder = WorkerBuilder::default(); c.wid("wid".to_string()); c.register("foobar", |_| async move { loop { diff --git a/tests/real/community.rs b/tests/real/community.rs index 736bef1f..2cc96903 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -1,7 +1,7 @@ extern crate faktory; use crate::skip_check; -use faktory::{Client, ConsumerBuilder, Job, JobBuilder}; +use faktory::{Client, Job, JobBuilder, WorkerBuilder}; use serde_json::Value; use std::{io, sync}; @@ -29,7 +29,7 @@ async fn process_order(j: Job) -> Result<(), std::io::Error> { async fn roundtrip() { skip_check!(); let jid = String::from("x-job-id-0123456782"); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.register("order", process_order); c.register("image", |job| async move { println!("{:?}", job); @@ -61,7 +61,7 @@ async fn multi() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); - let mut c = ConsumerBuilder::default_async(); + let mut c = WorkerBuilder::default_async(); c.hostname("tester".to_string()).wid(local.to_string()); c.register(local, move |j| { @@ -102,7 +102,7 @@ async fn fail() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(local.to_string()); c.register(local, move |j| { @@ -139,7 +139,7 @@ async fn queue() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(local.to_string()); c.register(local, move |_job| { let tx = sync::Arc::clone(&tx); @@ -233,7 +233,7 @@ async fn test_jobs_pushed_in_bulk() { // Let's check that the two well-formatted jobs // have _really_ been enqueued, i.e. that `enqueue_many` // is not an all-or-nothing operation: - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(local_3.to_string()); c.register("very_special", move |_job| async { Ok::<(), io::Error>(()) @@ -266,12 +266,12 @@ async fn test_jobs_created_with_builder() { skip_check!(); // prepare a client and a worker: - let mut client = Client::connect(None).await.unwrap(); - let mut consumer = ConsumerBuilder::default(); - consumer.register("rebuild_index", assert_args_empty); - consumer.register("register_order", assert_args_not_empty); + let mut cl = Client::connect(None).await.unwrap(); + let mut w = WorkerBuilder::default(); + w.register("rebuild_index", assert_args_empty); + w.register("register_order", assert_args_not_empty); - let mut consumer = consumer.connect(None).await.unwrap(); + let mut w = w.connect(None).await.unwrap(); // prepare some jobs with JobBuilder: let job1 = JobBuilder::new("rebuild_index") @@ -287,24 +287,24 @@ async fn test_jobs_created_with_builder() { job3.queue = "test_jobs_created_with_builder_1".to_string(); // enqueue ... - client.enqueue(job1).await.unwrap(); - client.enqueue(job2).await.unwrap(); - client.enqueue(job3).await.unwrap(); + cl.enqueue(job1).await.unwrap(); + cl.enqueue(job2).await.unwrap(); + cl.enqueue(job3).await.unwrap(); // ... and execute: - let had_job = consumer + let had_job = w .run_one(0, &["test_jobs_created_with_builder_0"]) .await .unwrap(); assert!(had_job); - let had_job = consumer + let had_job = w .run_one(0, &["test_jobs_created_with_builder_1"]) .await .unwrap(); assert!(had_job); - let had_job = consumer + let had_job = w .run_one(0, &["test_jobs_created_with_builder_1"]) .await .unwrap(); diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index 5d1e104b..6616a695 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -44,7 +44,7 @@ async fn ent_expiring_job() { // prepare a client and a worker: let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.register("AnExpiringJob", print_job); let mut c = c.connect(Some(&url)).await.unwrap(); @@ -91,9 +91,9 @@ async fn ent_unique_job() { let job_type = "order"; - // prepare producer and consumer: + // prepare client and worker: let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.register(job_type, print_job); let mut c = c.connect(Some(&url)).await.unwrap(); @@ -204,13 +204,13 @@ async fn ent_unique_job_until_success() { let url1 = url.clone(); let handle = tokio::spawn(async move { - // prepare producer and consumer, where the former can + // prepare client and worker, where the former can // send a job difficulty level as a job's args and the lattter // will sleep for a corresponding period of time, pretending // to work hard: - let mut producer_a = Client::connect(Some(&url1)).await.unwrap(); - let mut consumer_a = ConsumerBuilder::default_async(); - consumer_a.register(job_type, |job| async move { + let mut client_a = Client::connect(Some(&url1)).await.unwrap(); + let mut worker_a = WorkerBuilder::default_async(); + worker_a.register(job_type, |job| async move { let args = job.args().to_owned(); let mut args = args.iter(); let diffuculty_level = args @@ -223,15 +223,15 @@ async fn ent_unique_job_until_success() { eprintln!("{:?}", job); Ok::<(), io::Error>(()) }); - let mut consumer_a = consumer_a.connect(Some(&url1)).await.unwrap(); + let mut worker_a = worker_a.connect(Some(&url1)).await.unwrap(); let job = JobBuilder::new(job_type) .args(vec![difficulty_level]) .queue(queue_name) .unique_for(unique_for) .unique_until_success() // Faktory's default .build(); - producer_a.enqueue(job).await.unwrap(); - let had_job = consumer_a.run_one(0, &[queue_name]).await.unwrap(); + client_a.enqueue(job).await.unwrap(); + let had_job = worker_a.run_one(0, &[queue_name]).await.unwrap(); assert!(had_job); }); @@ -239,7 +239,7 @@ async fn ent_unique_job_until_success() { time::sleep(time::Duration::from_secs(1)).await; // continue - let mut producer_b = Client::connect(Some(&url)).await.unwrap(); + let mut client_b = Client::connect(Some(&url)).await.unwrap(); // this one is a 'duplicate' because the job is still // being executed in the spawned thread: @@ -250,7 +250,7 @@ async fn ent_unique_job_until_success() { .build(); // as a result: - let res = producer_b.enqueue(job).await.unwrap_err(); + let res = client_b.enqueue(job).await.unwrap_err(); if let error::Error::Protocol(error::Protocol::UniqueConstraintViolation { msg }) = res { assert_eq!(msg, "Job not unique"); } else { @@ -261,7 +261,7 @@ async fn ent_unique_job_until_success() { // Now that the job submitted in a spawned thread has been successfully executed // (with ACK sent to server), the producer 'B' can push another one: - producer_b + client_b .enqueue( JobBuilder::new(job_type) .args(vec![difficulty_level]) @@ -288,9 +288,9 @@ async fn ent_unique_job_until_start() { let url1 = url.clone(); let handle = tokio::spawn(async move { - let mut producer_a = Client::connect(Some(&url1)).await.unwrap(); - let mut consumer_a = ConsumerBuilder::default_async(); - consumer_a.register(job_type, |job| async move { + let mut client_a = Client::connect(Some(&url1)).await.unwrap(); + let mut worker_a = WorkerBuilder::default_async(); + worker_a.register(job_type, |job| async move { let args = job.args().to_owned(); let mut args = args.iter(); let diffuculty_level = args @@ -303,8 +303,8 @@ async fn ent_unique_job_until_start() { eprintln!("{:?}", job); Ok::<(), io::Error>(()) }); - let mut consumer_a = consumer_a.connect(Some(&url1)).await.unwrap(); - producer_a + let mut worker_a = worker_a.connect(Some(&url1)).await.unwrap(); + client_a .enqueue( JobBuilder::new(job_type) .args(vec![difficulty_level]) @@ -316,7 +316,7 @@ async fn ent_unique_job_until_start() { .await .unwrap(); // as soon as the job is fetched, the unique lock gets released - let had_job = consumer_a.run_one(0, &[queue_name]).await.unwrap(); + let had_job = worker_a.run_one(0, &[queue_name]).await.unwrap(); assert!(had_job); }); @@ -324,8 +324,8 @@ async fn ent_unique_job_until_start() { time::sleep(time::Duration::from_secs(1)).await; // the unique lock has been released by this time, so the job is enqueued successfully: - let mut producer_b = Client::connect(Some(&url)).await.unwrap(); - producer_b + let mut client_b = Client::connect(Some(&url)).await.unwrap(); + client_b .enqueue( JobBuilder::new(job_type) .args(vec![difficulty_level]) @@ -379,7 +379,7 @@ async fn ent_unique_job_bypass_unique_lock() { // let's consume three times from the queue to verify that the first two jobs // have been enqueued for real, while the last one has not. - let mut c = ConsumerBuilder::default_async(); + let mut c = WorkerBuilder::default_async(); c.register("order", print_job); let mut c = c.connect(Some(&url)).await.unwrap(); @@ -424,7 +424,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { p.enqueue(job_tackable).await.expect("enqueued"); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); { let job_id = job_id.clone(); @@ -495,7 +495,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { // 'Faktory' will be keeping last known update for at least 30 minutes: assert_eq!(progress.percent, Some(33)); - // But it actually knows the job's real status, since the consumer (worker) + // But it actually knows the job's real status, since the worker // informed it immediately after finishing with the job: assert_eq!(progress.state, JobState::Success); @@ -559,7 +559,7 @@ async fn test_batch_of_jobs_can_be_initiated() { let url = learn_faktory_url(); let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.register("thumbnail", |_job| async { Ok::<(), io::Error>(()) }); c.register("clean_up", |_job| async { Ok(()) }); let mut c = c.connect(Some(&url)).await.unwrap(); @@ -694,9 +694,9 @@ async fn test_batches_can_be_nested() { skip_if_not_enterprise!(); let url = learn_faktory_url(); - // Set up 'producer', 'consumer', and 'tracker': + // Set up 'client', 'worker', and 'tracker': let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.register("jobtype", |_job| async { Ok::<(), io::Error>(()) }); let mut _c = c.connect(Some(&url)).await.unwrap(); let mut t = Client::connect(Some(&url)) @@ -794,13 +794,13 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { skip_if_not_enterprise!(); let url = learn_faktory_url(); - // prepare a producer, a consumer of 'order' jobs, and a tracker: - let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut c = ConsumerBuilder::default(); - c.register("order", |_job| async { Ok(()) }); - c.register("order_clean_up", |_job| async { Ok::<(), io::Error>(()) }); - let mut c = c.connect(Some(&url)).await.unwrap(); - let mut t = Client::connect(Some(&url)).await.unwrap(); + // prepare a client, a worker of 'order' jobs, and a tracker: + let mut cl = Client::connect(Some(&url)).await.unwrap(); + let mut tr = Client::connect(Some(&url)).await.unwrap(); + let mut w = WorkerBuilder::default(); + w.register("order", |_job| async { Ok(()) }); + w.register("order_clean_up", |_job| async { Ok::<(), io::Error>(()) }); + let mut c = w.connect(Some(&url)).await.unwrap(); let mut jobs = some_jobs( "order", @@ -814,7 +814,7 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { ); // start a 'batch': - let mut b = p + let mut b = cl .start_batch( Batch::builder() .description("Orders processing workload") @@ -830,7 +830,7 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { } // check this batch's status: - let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); + let s = tr.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.total, 3); assert_eq!(s.pending, 3); assert_eq!(s.success_callback_state, CallbackState::Pending); @@ -850,7 +850,7 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { ); // check this batch's status again: - let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); + let s = tr.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.total, 3); assert_eq!(s.pending, 0); assert_eq!(s.failed, 0); @@ -866,7 +866,7 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { b.commit().await.unwrap(); // ... and check batch status: - let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); + let s = cl.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.success_callback_state, CallbackState::Enqueued); // finally, let's consume from the success callbacks queue ... @@ -876,7 +876,7 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { ); // ... and see the final status: - let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); + let s = cl.get_batch_status(bid.clone()).await.unwrap().unwrap(); assert_eq!(s.success_callback_state, CallbackState::FinishedOk); } @@ -886,14 +886,14 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { skip_if_not_enterprise!(); let url = learn_faktory_url(); - let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut t = Client::connect(Some(&url)).await.unwrap(); + let mut cl = Client::connect(Some(&url)).await.unwrap(); + let mut tracker = Client::connect(Some(&url)).await.unwrap(); let q_name = "test_callback_will_be_queued_upon_commit_even_if_batch_is_empty"; let complete_cb_jobtype = "complete_callback_jobtype"; let success_cb_jobtype = "success_cb_jobtype"; let complete_cb = some_jobs(complete_cb_jobtype, q_name, 1).next().unwrap(); let success_cb = some_jobs(success_cb_jobtype, q_name, 1).next().unwrap(); - let b = p + let b = cl .start_batch( Batch::builder() .description("Orders processing workload") @@ -903,7 +903,11 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { .unwrap(); let bid = b.id().to_owned(); - let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); + let s = tracker + .get_batch_status(bid.clone()) + .await + .unwrap() + .unwrap(); assert_eq!(s.total, 0); // no jobs in the batch; assert_eq!(s.success_callback_state, CallbackState::Pending); assert_eq!(s.complete_callback_state, CallbackState::Pending); @@ -913,7 +917,11 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { // let's give the Faktory server some time: thread::sleep(time::Duration::from_secs(2)); - let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); + let s = tracker + .get_batch_status(bid.clone()) + .await + .unwrap() + .unwrap(); assert_eq!(s.total, 0); // again, there are no jobs in the batch ... // The docs say "If you don't push any jobs into the batch, any callbacks will fire immediately upon BATCH COMMIT." @@ -921,7 +929,7 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { assert_eq!(s.complete_callback_state, CallbackState::Enqueued); assert_eq!(s.success_callback_state, CallbackState::Pending); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.register(complete_cb_jobtype, |_job| async { Ok(()) }); c.register(success_cb_jobtype, |_job| async { Err(io::Error::new( @@ -934,7 +942,11 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { assert_had_one!(&mut c, q_name); // complete callback consumed - let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); + let s = tracker + .get_batch_status(bid.clone()) + .await + .unwrap() + .unwrap(); assert_eq!(s.total, 0); match s.complete_callback_state { CallbackState::FinishedOk => {} @@ -946,7 +958,11 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { } assert_had_one!(&mut c, q_name); // success callback consumed - let s = t.get_batch_status(bid.clone()).await.unwrap().unwrap(); + let s = tracker + .get_batch_status(bid.clone()) + .await + .unwrap() + .unwrap(); assert_eq!(s.total, 0); assert_eq!(s.complete_callback_state, CallbackState::FinishedOk); // Still `Enqueued` due to the fact that it was not finished with success. diff --git a/tests/tls.rs b/tests/tls.rs index 56c4ded4..54b175e8 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -27,7 +27,7 @@ async fn roundtrip_tls() { let local = "roundtrip_tls"; let (tx, rx) = sync::mpsc::channel(); - let mut c = ConsumerBuilder::default(); + let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(local.to_string()); c.register_runner(local, fixtures::JobHandler::new(tx)); From a1c613aac1b536fb25fcd9b09567921b9f3d333c Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Tue, 27 Feb 2024 00:47:13 +0500 Subject: [PATCH 015/129] Add JobId struct --- src/proto/single/id.rs | 48 +++++++++++++++++++++++++++++++++++++++++ src/proto/single/mod.rs | 14 ++++++------ 2 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 src/proto/single/id.rs diff --git a/src/proto/single/id.rs b/src/proto/single/id.rs new file mode 100644 index 00000000..36e23f2d --- /dev/null +++ b/src/proto/single/id.rs @@ -0,0 +1,48 @@ +use super::utils; +use std::ops::{Deref, DerefMut}; + +/// Job identifier. +/// +/// The Faktory server expects a [`jid`](struct.Job.html#structfield.jid) of a reasonable length +/// (at least 8 chars), which you should take into account when creating a new instance of `JobId`. +/// If you do not have any domain, product or organisation specific requirements, you may prefer +/// to have a random job identifier generated for you with [`random`](JobId::random). +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct JobId(String); + +impl JobId { + /// Internally, generates a 16-char long random string. + pub fn random() -> Self { + Self(utils::gen_random_jid()) + } +} + +impl From for JobId +where + S: AsRef, +{ + fn from(value: S) -> Self { + JobId(value.as_ref().to_owned()) + } +} + +impl Deref for JobId { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for JobId { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Into for JobId { + fn into(self) -> String { + self.0 + } +} + +// ----------------------------------------------------- diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index 6fdc3a86..ce0582a1 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use tokio::io::{AsyncBufRead, AsyncWriteExt}; mod cmd; +mod id; mod resp; mod utils; @@ -12,8 +13,9 @@ mod utils; #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] pub mod ent; -pub use self::cmd::*; -pub use self::resp::*; +pub use cmd::*; +pub use id::JobId; +pub use resp::*; pub(crate) use self::utils::gen_random_wid; @@ -66,8 +68,8 @@ const JOB_DEFAULT_BACKTRACE: usize = 0; )] pub struct Job { /// The job's unique identifier. - #[builder(default = "utils::gen_random_jid()")] - pub(crate) jid: String, + #[builder(default = "JobId::random()")] + pub(crate) jid: JobId, /// The queue this job belongs to. Usually `default`. #[builder(default = "JOB_DEFAULT_QUEUE.into()")] @@ -236,7 +238,7 @@ impl Job { } /// This job's id. - pub fn id(&self) -> &str { + pub fn id(&self) -> &JobId { &self.jid } @@ -284,7 +286,7 @@ mod test { let job_args = vec!["ISBN-13:9781718501850"]; let job = JobBuilder::new(job_kind).args(job_args.clone()).build(); - assert!(job.jid != "".to_owned()); + assert!(job.jid != "".into()); assert!(job.queue == JOB_DEFAULT_QUEUE.to_string()); assert_eq!(job.kind, job_kind); assert_eq!(job.args, job_args); From 008126d024e969cecf5412fc3f0f332955193f6b Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Tue, 27 Feb 2024 10:15:47 +0500 Subject: [PATCH 016/129] Use JobId in lib --- src/lib.rs | 2 +- src/proto/client/ent.rs | 5 +++-- src/proto/client/mod.rs | 10 +++++----- src/proto/mod.rs | 4 +++- src/proto/single/cmd.rs | 25 +++++++++++-------------- src/proto/single/ent/cmd.rs | 4 ++-- src/proto/single/ent/progress.rs | 18 ++++++++++-------- src/proto/single/id.rs | 6 ------ src/worker/mod.rs | 10 +++++----- src/worker/registries.rs | 10 +++++----- tests/real/enterprise.rs | 4 ++-- 11 files changed, 47 insertions(+), 51 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9d41334e..db6f2076 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,7 @@ mod proto; mod worker; pub use crate::error::Error; -pub use crate::proto::{Client, Job, JobBuilder, Reconnect}; +pub use crate::proto::{Client, Job, JobBuilder, JobId, Reconnect}; pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; #[cfg(feature = "ent")] diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index cc252a3a..d9375dd6 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -1,5 +1,6 @@ use super::super::{ - single, BatchStatus, CommitBatch, GetBatchStatus, OpenBatch, Progress, ProgressUpdate, Track, + single, BatchStatus, CommitBatch, GetBatchStatus, JobId, OpenBatch, Progress, ProgressUpdate, + Track, }; use super::{Client, ReadToken}; use crate::ent::{Batch, BatchHandle}; @@ -14,7 +15,7 @@ impl Client { } /// Fetch information on a job's execution progress from Faktory. - pub async fn get_progress(&mut self, jid: String) -> Result, Error> { + pub async fn get_progress(&mut self, jid: JobId) -> Result, Error> { let cmd = Track::Get(jid); self.issue(&cmd).await?.read_json().await } diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index b11eb214..d74c098b 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -102,8 +102,8 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{Client, ent::JobState}; -/// let job_id = String::from("W8qyVle9vXzUWQOf"); +/// use faktory::{Client, JobId, ent::JobState}; +/// let job_id = JobId::from("W8qyVle9vXzUWQOf"); /// let mut cl = Client::connect(None).await?; /// if let Some(progress) = cl.get_progress(job_id).await? { /// if let JobState::Success = progress.state { @@ -120,10 +120,10 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{Client, ent::ProgressUpdateBuilder}; -/// let jid = String::from("W8qyVle9vXzUWQOf"); +/// use faktory::{Client, JobId, ent::ProgressUpdateBuilder}; +/// let jid = JobId::from("W8qyVle9vXzUWQOf"); /// let mut cl = Client::connect(None).await?; -/// let progress = ProgressUpdateBuilder::new(&jid) +/// let progress = ProgressUpdateBuilder::new(jid) /// .desc("Almost done...".to_owned()) /// .percent(99) /// .build(); diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 3db996dc..72d5a178 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -9,7 +9,9 @@ pub(crate) use client::{ClientOptions, HeartbeatStatus, EXPECTED_PROTOCOL_VERSIO mod single; -pub use single::{Ack, Fail, Info, Job, JobBuilder, Push, PushBulk, QueueAction, QueueControl}; +pub use single::{ + Ack, Fail, Info, Job, JobBuilder, JobId, Push, PushBulk, QueueAction, QueueControl, +}; pub(crate) mod utils; #[cfg(feature = "ent")] diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index ff531e05..45641aaf 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -1,4 +1,5 @@ -use crate::{error::Error, Job}; +use crate::error::Error; +use crate::proto::{Job, JobId}; use std::error::Error as StdError; use tokio::io::AsyncWriteExt; @@ -53,13 +54,13 @@ impl FaktoryCommand for Info { #[derive(Serialize)] pub struct Ack { #[serde(rename = "jid")] - job_id: String, + job_id: JobId, } -impl Ack { - pub fn new>(job_id: S) -> Ack { +impl From<&JobId> for Ack { + fn from(job_id: &JobId) -> Self { Ack { - job_id: job_id.into(), + job_id: job_id.to_owned(), } } } @@ -86,7 +87,7 @@ self_to_cmd!(Heartbeat, "BEAT"); #[derive(Serialize, Clone)] pub struct Fail { #[serde(rename = "jid")] - job_id: String, + job_id: JobId, #[serde(rename = "errtype")] kind: String, message: String, @@ -95,13 +96,9 @@ pub struct Fail { } impl Fail { - pub fn new, S2: Into, S3: Into>( - job_id: S1, - kind: S2, - message: S3, - ) -> Self { + pub fn new(job_id: JobId, kind: impl Into, message: impl Into) -> Self { Fail { - job_id: job_id.into(), + job_id, kind: kind.into(), message: message.into(), backtrace: Vec::new(), @@ -109,7 +106,7 @@ impl Fail { } // "unknown" is the errtype used by the go library too - pub fn generic, S2: Into>(job_id: S1, message: S2) -> Self { + pub fn generic>(job_id: JobId, message: S) -> Self { Fail::new(job_id, "unknown", message) } @@ -117,7 +114,7 @@ impl Fail { self.backtrace = lines; } - pub fn generic_with_backtrace(jid: String, e: E) -> Self + pub fn generic_with_backtrace(jid: JobId, e: E) -> Self where E: StdError, { diff --git a/src/proto/single/ent/cmd.rs b/src/proto/single/ent/cmd.rs index b522bb89..acb38c8f 100644 --- a/src/proto/single/ent/cmd.rs +++ b/src/proto/single/ent/cmd.rs @@ -1,12 +1,12 @@ use super::ProgressUpdate; use crate::error::Error; -use crate::proto::single::FaktoryCommand; +use crate::proto::{single::FaktoryCommand, JobId}; use tokio::io::AsyncWriteExt; #[derive(Debug, Clone)] pub enum Track { Set(ProgressUpdate), - Get(String), + Get(JobId), } #[async_trait::async_trait] diff --git a/src/proto/single/ent/progress.rs b/src/proto/single/ent/progress.rs index ede5d838..49304a5a 100644 --- a/src/proto/single/ent/progress.rs +++ b/src/proto/single/ent/progress.rs @@ -1,3 +1,5 @@ +use crate::proto::single::JobId; + use super::utils; use chrono::{DateTime, Utc}; use derive_builder::Builder; @@ -15,7 +17,7 @@ use derive_builder::Builder; pub struct ProgressUpdate { /// Id of the tracked job. #[builder(setter(custom))] - pub jid: String, + pub jid: JobId, /// Percentage of the job's completion. #[serde(skip_serializing_if = "Option::is_none")] @@ -38,7 +40,7 @@ pub struct ProgressUpdate { impl ProgressUpdate { /// Create an instance of `ProgressUpdate` for the job with this ID specifying its completion percentage. - pub fn set(jid: impl Into, percent: u8) -> ProgressUpdate { + pub fn set(jid: JobId, percent: u8) -> ProgressUpdate { ProgressUpdate::builder(jid).percent(percent).build() } @@ -46,7 +48,7 @@ impl ProgressUpdate { /// /// Equivalent to creating a [new](struct.ProgressUpdateBuilder.html#method.new) /// `ProgressUpdateBuilder`. - pub fn builder(jid: impl Into) -> ProgressUpdateBuilder { + pub fn builder(jid: JobId) -> ProgressUpdateBuilder { ProgressUpdateBuilder::new(jid) } } @@ -59,9 +61,9 @@ impl ProgressUpdateBuilder { } /// Create a new instance of `JobBuilder` - pub fn new(jid: impl Into) -> ProgressUpdateBuilder { + pub fn new(jid: JobId) -> ProgressUpdateBuilder { ProgressUpdateBuilder { - jid: Some(jid.into()), + jid: Some(jid), ..ProgressUpdateBuilder::create_empty() } } @@ -120,7 +122,7 @@ impl std::fmt::Display for JobState { #[derive(Debug, Clone, Deserialize)] pub struct Progress { /// Id of the tracked job. - pub jid: String, + pub jid: JobId, /// Job's state. pub state: JobState, @@ -141,7 +143,7 @@ impl Progress { /// /// This will copy the [`desc`](Progress::desc) from the `Progress` (retrieved) over to `ProgressUpdate` (to be sent). pub fn update_percent(&self, percent: u8) -> ProgressUpdate { - ProgressUpdate::builder(&self.jid) + ProgressUpdate::builder(self.jid.clone()) .desc(self.desc.clone()) .percent(percent) .build() @@ -149,6 +151,6 @@ impl Progress { /// Create an instance of `ProgressUpdateBuilder` for the job. pub fn update_builder(&self) -> ProgressUpdateBuilder { - ProgressUpdateBuilder::new(&self.jid) + ProgressUpdateBuilder::new(self.jid.clone()) } } diff --git a/src/proto/single/id.rs b/src/proto/single/id.rs index 36e23f2d..95ea0ab8 100644 --- a/src/proto/single/id.rs +++ b/src/proto/single/id.rs @@ -39,10 +39,4 @@ impl DerefMut for JobId { } } -impl Into for JobId { - fn into(self) -> String { - self.0 - } -} - // ----------------------------------------------------- diff --git a/src/worker/mod.rs b/src/worker/mod.rs index 8d5449aa..16b52888 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -1,7 +1,7 @@ use super::proto::{Client, Reconnect}; use crate::{ proto::{Ack, Fail}, - Error, Job, + Error, Job, JobId, }; use std::sync::{atomic, Arc}; use std::{error::Error as StdError, sync::atomic::AtomicUsize}; @@ -178,8 +178,8 @@ impl) -> Result<(), Error> { - self.c.issue(&Ack::new(jid)).await?.read_ok().await + async fn report_success_to_server(&mut self, jid: JobId) -> Result<(), Error> { + self.c.issue(&Ack::from(&jid)).await?.read_ok().await } async fn report_on_all_workers(&mut self) -> Result<(), Error> { @@ -192,7 +192,7 @@ impl self.c.issue(&Ack::new(jid)).await, + Ok(ref jid) => self.c.issue(&Ack::from(jid)).await, Err(ref fail) => self.c.issue(fail).await, }; @@ -230,7 +230,7 @@ impl r.read_ok().await, Err(_) => continue, diff --git a/src/worker/registries.rs b/src/worker/registries.rs index 9cec6c4f..a10cebe2 100644 --- a/src/worker/registries.rs +++ b/src/worker/registries.rs @@ -1,5 +1,5 @@ use super::runner::BoxedJobRunner; -use crate::proto::Fail; +use crate::proto::{Fail, JobId}; use fnv::FnvHashMap; use std::{ ops::{Deref, DerefMut}, @@ -33,8 +33,8 @@ impl Default for CallbacksRegistry { #[derive(Default)] pub(crate) struct WorkerState { - pub(crate) last_job_result: Option>, - pub(crate) running_job: Option, + pub(crate) last_job_result: Option>, + pub(crate) running_job: Option, } pub(crate) struct StatesRegistry(Vec>); @@ -57,11 +57,11 @@ impl StatesRegistry { Self((0..workers_count).map(|_| Default::default()).collect()) } - pub(crate) fn register_running(&self, worker: usize, jid: String) { + pub(crate) fn register_running(&self, worker: usize, jid: JobId) { self[worker].lock().expect("lock acquired").running_job = Some(jid); } - pub(crate) fn register_success(&self, worker: usize, jid: String) { + pub(crate) fn register_success(&self, worker: usize, jid: JobId) { self[worker].lock().expect("lock acquired").last_job_result = Some(Ok(jid)); } diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index 6616a695..f2b70193 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -441,7 +441,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { // 'an internal server error occurred: tracking subsystem is only available in Faktory Enterprise' assert!(t .set_progress( - ProgressUpdate::builder(&job_id.clone()) + ProgressUpdate::builder(job_id.clone()) .desc("Still processing...".to_owned()) .percent(32) .build(), @@ -450,7 +450,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { .is_ok()); // Let's update the progress once again, to check the 'set_progress' shortcut: assert!(t - .set_progress(ProgressUpdate::set(&job_id.clone(), 33)) + .set_progress(ProgressUpdate::set(job_id.clone(), 33)) .await .is_ok()); From 9a56dea39ae44a14ef51bdac84833a23eb90d6f4 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Tue, 27 Feb 2024 10:50:47 +0500 Subject: [PATCH 017/129] Use WorkerId new-type --- src/lib.rs | 2 +- src/proto/client/mod.rs | 6 ++-- src/proto/client/options.rs | 4 ++- src/proto/mod.rs | 2 +- src/proto/single/cmd.rs | 8 ++--- src/proto/single/id.rs | 64 ++++++++++++++++++++++++++----------- src/proto/single/mod.rs | 4 +-- src/worker/builder.rs | 4 +-- tests/consumer.rs | 10 +++--- tests/real/community.rs | 8 ++--- tests/tls.rs | 2 +- 11 files changed, 71 insertions(+), 43 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index db6f2076..5e765d30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,7 @@ mod proto; mod worker; pub use crate::error::Error; -pub use crate::proto::{Client, Job, JobBuilder, JobId, Reconnect}; +pub use crate::proto::{Client, Job, JobBuilder, JobId, Reconnect, WorkerId}; pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; #[cfg(feature = "ent")] diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index d74c098b..f3461c17 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -8,7 +8,7 @@ use crate::proto::{BatchStatus, Progress, ProgressUpdate}; use super::{single, Info, Push, QueueAction, QueueControl, Reconnect}; use super::{utils, PushBulk}; use crate::error::{self, Error}; -use crate::Job; +use crate::{Job, WorkerId}; use std::collections::HashMap; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; use tokio::io::{AsyncRead, AsyncWrite, BufStream}; @@ -254,7 +254,7 @@ where self.opts.hostname = Some(hostname); let pid = self.opts.pid.unwrap_or_else(|| std::process::id() as usize); self.opts.pid = Some(pid); - let wid = self.opts.wid.clone().unwrap_or_else(single::gen_random_wid); + let wid = self.opts.wid.clone().unwrap_or_else(WorkerId::random); self.opts.wid = Some(wid); hello.hostname = Some(self.opts.hostname.clone().unwrap()); @@ -375,7 +375,7 @@ where pub(crate) async fn heartbeat(&mut self) -> Result { single::write_command( &mut self.stream, - &single::Heartbeat::new(&**self.opts.wid.as_ref().unwrap()), + &single::Heartbeat::new(self.opts.wid.as_ref().unwrap().clone()), ) .await?; diff --git a/src/proto/client/options.rs b/src/proto/client/options.rs index 113cf48e..71b432fb 100644 --- a/src/proto/client/options.rs +++ b/src/proto/client/options.rs @@ -1,3 +1,5 @@ +use crate::proto::WorkerId; + #[derive(Clone)] pub(crate) struct ClientOptions { /// Hostname to advertise to server. @@ -10,7 +12,7 @@ pub(crate) struct ClientOptions { /// Worker ID to advertise to server. /// Defaults to a GUID. - pub(crate) wid: Option, + pub(crate) wid: Option, /// Labels to advertise to se/// A stream that can be re-established after failing.rver. /// Defaults to ["rust"]. diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 72d5a178..acc98dab 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -10,7 +10,7 @@ pub(crate) use client::{ClientOptions, HeartbeatStatus, EXPECTED_PROTOCOL_VERSIO mod single; pub use single::{ - Ack, Fail, Info, Job, JobBuilder, JobId, Push, PushBulk, QueueAction, QueueControl, + Ack, Fail, Info, Job, JobBuilder, JobId, Push, PushBulk, QueueAction, QueueControl, WorkerId, }; pub(crate) mod utils; diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index 45641aaf..03e3cdc6 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -1,5 +1,5 @@ use crate::error::Error; -use crate::proto::{Job, JobId}; +use crate::proto::{Job, JobId, WorkerId}; use std::error::Error as StdError; use tokio::io::AsyncWriteExt; @@ -71,11 +71,11 @@ self_to_cmd!(Ack, "ACK"); #[derive(Serialize)] pub struct Heartbeat { - wid: String, + wid: WorkerId, } impl Heartbeat { - pub fn new>(wid: S) -> Heartbeat { + pub fn new>(wid: S) -> Heartbeat { Heartbeat { wid: wid.into() } } } @@ -180,7 +180,7 @@ pub struct Hello { #[serde(skip_serializing_if = "Option::is_none")] pub hostname: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub wid: Option, + pub wid: Option, #[serde(skip_serializing_if = "Option::is_none")] pub pid: Option, #[serde(skip_serializing_if = "Vec::is_empty")] diff --git a/src/proto/single/id.rs b/src/proto/single/id.rs index 95ea0ab8..b5fc216f 100644 --- a/src/proto/single/id.rs +++ b/src/proto/single/id.rs @@ -1,42 +1,70 @@ use super::utils; use std::ops::{Deref, DerefMut}; +macro_rules! string_wrapper_impls { + ($new_type:ident) => { + impl From for $new_type + where + S: AsRef, + { + fn from(value: S) -> Self { + $new_type(value.as_ref().to_owned()) + } + } + + impl Deref for $new_type { + type Target = String; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for $new_type { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + }; +} + /// Job identifier. /// /// The Faktory server expects a [`jid`](struct.Job.html#structfield.jid) of a reasonable length /// (at least 8 chars), which you should take into account when creating a new instance of `JobId`. +/// /// If you do not have any domain, product or organisation specific requirements, you may prefer /// to have a random job identifier generated for you with [`random`](JobId::random). #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct JobId(String); impl JobId { - /// Internally, generates a 16-char long random string. + /// Internally, generates a 16-char long random ASCII string. pub fn random() -> Self { Self(utils::gen_random_jid()) } } -impl From for JobId -where - S: AsRef, -{ - fn from(value: S) -> Self { - JobId(value.as_ref().to_owned()) - } -} +string_wrapper_impls!(JobId); -impl Deref for JobId { - type Target = String; - fn deref(&self) -> &Self::Target { - &self.0 - } -} +// ----------------------------------------------------- + +/// Worker identifier. +/// +/// The Faktory server expects a non-empty string as a worker identifier, +/// see [`wid`](struct.WorkerBuilder.html#method.wid). +/// +/// If you do not have any domain, product or organisation specific requirements, you may prefer +/// to have a random job identifier generated for you with [`random`](WorkerId::random). +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct WorkerId(String); -impl DerefMut for JobId { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 +impl WorkerId { + /// Internally, generates a 32-char long random ASCII string. + pub fn random() -> Self { + Self(utils::gen_random_wid()) } } +string_wrapper_impls!(WorkerId); + // ----------------------------------------------------- diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index ce0582a1..c3e1cfb3 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -14,11 +14,9 @@ mod utils; pub mod ent; pub use cmd::*; -pub use id::JobId; +pub use id::{JobId, WorkerId}; pub use resp::*; -pub(crate) use self::utils::gen_random_wid; - const JOB_DEFAULT_QUEUE: &str = "default"; const JOB_DEFAULT_RESERVED_FOR_SECS: usize = 600; const JOB_DEFAULT_RETRY_COUNT: isize = 25; diff --git a/src/worker/builder.rs b/src/worker/builder.rs index eb417ec7..7b95ef95 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -1,7 +1,7 @@ use super::{runner::Closure, CallbacksRegistry, Client, Worker}; use crate::{ proto::{utils, ClientOptions}, - Error, Job, JobRunner, + Error, Job, JobRunner, WorkerId, }; use std::future::Future; use tokio::io::{AsyncRead, AsyncWrite, BufStream}; @@ -48,7 +48,7 @@ impl WorkerBuilder { /// Set a unique identifier for this worker. /// /// Defaults to a randomly generated 32-char ASCII string. - pub fn wid(&mut self, wid: String) -> &mut Self { + pub fn wid(&mut self, wid: WorkerId) -> &mut Self { self.opts.wid = Some(wid); self } diff --git a/tests/consumer.rs b/tests/consumer.rs index d615c841..f6fade87 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -13,7 +13,7 @@ async fn hello() { let mut s = mock::Stream::default(); let mut c: WorkerBuilder = WorkerBuilder::default(); c.hostname("host".to_string()) - .wid("wid".to_string()) + .wid("wid".into()) .labels(vec!["foo".to_string(), "bar".to_string()]); c.register("never_called", |_j: Job| async move { unreachable!() }); let c = c.connect_with(s.clone(), None).await.unwrap(); @@ -156,7 +156,7 @@ async fn dequeue_first_empty() { async fn well_behaved() { let mut s = mock::Stream::new(2); // main plus worker let mut c = WorkerBuilder::default(); - c.wid("wid".to_string()); + c.wid("wid".into()); c.register("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; @@ -221,7 +221,7 @@ async fn well_behaved() { async fn no_first_job() { let mut s = mock::Stream::new(2); let mut c = WorkerBuilder::default(); - c.wid("wid".to_string()); + c.wid("wid".into()); c.register("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; @@ -288,7 +288,7 @@ async fn well_behaved_many() { let mut s = mock::Stream::new(3); let mut c = WorkerBuilder::default(); c.workers(2); - c.wid("wid".to_string()); + c.wid("wid".into()); c.register("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; @@ -363,7 +363,7 @@ async fn well_behaved_many() { async fn terminate() { let mut s = mock::Stream::new(2); let mut c: WorkerBuilder = WorkerBuilder::default(); - c.wid("wid".to_string()); + c.wid("wid".into()); c.register("foobar", |_| async move { loop { sleep(Duration::from_secs(5)).await; diff --git a/tests/real/community.rs b/tests/real/community.rs index 2cc96903..6854efd0 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -62,7 +62,7 @@ async fn multi() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); let mut c = WorkerBuilder::default_async(); - c.hostname("tester".to_string()).wid(local.to_string()); + c.hostname("tester".to_string()).wid(local.into()); c.register(local, move |j| { let tx = sync::Arc::clone(&tx); @@ -103,7 +103,7 @@ async fn fail() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local.to_string()); + c.hostname("tester".to_string()).wid(local.into()); c.register(local, move |j| { let tx = sync::Arc::clone(&tx); @@ -140,7 +140,7 @@ async fn queue() { let tx = sync::Arc::new(sync::Mutex::new(tx)); let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local.to_string()); + c.hostname("tester".to_string()).wid(local.into()); c.register(local, move |_job| { let tx = sync::Arc::clone(&tx); Box::pin(async move { tx.lock().unwrap().send(true) }) @@ -234,7 +234,7 @@ async fn test_jobs_pushed_in_bulk() { // have _really_ been enqueued, i.e. that `enqueue_many` // is not an all-or-nothing operation: let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local_3.to_string()); + c.hostname("tester".to_string()).wid(local_3.into()); c.register("very_special", move |_job| async { Ok::<(), io::Error>(()) }); diff --git a/tests/tls.rs b/tests/tls.rs index 54b175e8..13f7bc8f 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -29,7 +29,7 @@ async fn roundtrip_tls() { let (tx, rx) = sync::mpsc::channel(); let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local.to_string()); + c.hostname("tester".to_string()).wid(local.into()); c.register_runner(local, fixtures::JobHandler::new(tx)); let tls = || async { From 566d2a2014b5dde45779ad965d3fbd2193961e52 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Tue, 27 Feb 2024 11:15:07 +0500 Subject: [PATCH 018/129] Split Batch mod into logic constructs --- src/proto/batch/handle.rs | 46 +++++++++++++ src/proto/batch/mod.rs | 140 +++----------------------------------- src/proto/batch/status.rs | 91 +++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 132 deletions(-) create mode 100644 src/proto/batch/handle.rs create mode 100644 src/proto/batch/status.rs diff --git a/src/proto/batch/handle.rs b/src/proto/batch/handle.rs new file mode 100644 index 00000000..1a1f0327 --- /dev/null +++ b/src/proto/batch/handle.rs @@ -0,0 +1,46 @@ +use super::Batch; +use crate::{Client, Error, Job}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; + +/// Represents a newly started or re-opened batch of jobs. +pub struct BatchHandle<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> { + bid: String, + c: &'a mut Client, +} + +impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { + /// ID issued by the Faktory server to this batch. + pub fn id(&self) -> &str { + self.bid.as_ref() + } + + pub(crate) fn new(bid: String, c: &mut Client) -> BatchHandle<'_, S> { + BatchHandle { bid, c } + } + + /// Add the given job to the batch. + /// + /// Should the submitted job - for whatever reason - already have a `bid` key present in its custom hash, + /// this value will be overwritten by the ID of the batch this job is being added to with the old value + /// returned as `Some()`. + pub async fn add(&mut self, mut job: Job) -> Result, Error> { + let bid = job.custom.insert("bid".into(), self.bid.clone().into()); + self.c.enqueue(job).await.map(|_| bid) + } + + /// Initiate a child batch of jobs. + pub async fn start_batch(&mut self, mut batch: Batch) -> Result, Error> { + batch.parent_bid = Some(self.bid.clone()); + self.c.start_batch(batch).await + } + + /// Commit this batch. + /// + /// The Faktory server will not queue any callbacks, unless the batch is committed. + /// Committing an empty batch will make the server queue the callback(s) right away. + /// Once committed, the batch can still be re-opened with [open_batch](Client::open_batch), + /// and extra jobs can be added to it. + pub async fn commit(self) -> Result<(), Error> { + self.c.commit_batch(self.bid).await + } +} diff --git a/src/proto/batch/mod.rs b/src/proto/batch/mod.rs index ffc9c7ba..86c98bad 100644 --- a/src/proto/batch/mod.rs +++ b/src/proto/batch/mod.rs @@ -1,11 +1,16 @@ -use crate::{Client, Error, Job}; -use chrono::{DateTime, Utc}; +#[cfg(doc)] +use crate::Client; + +use crate::Job; use derive_builder::Builder; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; mod cmd; +mod handle; +mod status; pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; +pub use handle::BatchHandle; +pub use status::{BatchStatus, CallbackState}; /// Batch of jobs. /// @@ -208,135 +213,6 @@ impl Clone for BatchBuilder { } } -/// Represents a newly started or re-opened batch of jobs. -pub struct BatchHandle<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> { - bid: String, - c: &'a mut Client, -} - -impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { - /// ID issued by the Faktory server to this batch. - pub fn id(&self) -> &str { - self.bid.as_ref() - } - - pub(crate) fn new(bid: String, c: &mut Client) -> BatchHandle<'_, S> { - BatchHandle { bid, c } - } - - /// Add the given job to the batch. - /// - /// Should the submitted job - for whatever reason - already have a `bid` key present in its custom hash, - /// this value will be overwritten by the ID of the batch this job is being added to with the old value - /// returned as `Some()`. - pub async fn add(&mut self, mut job: Job) -> Result, Error> { - let bid = job.custom.insert("bid".into(), self.bid.clone().into()); - self.c.enqueue(job).await.map(|_| bid) - } - - /// Initiate a child batch of jobs. - pub async fn start_batch(&mut self, mut batch: Batch) -> Result, Error> { - batch.parent_bid = Some(self.bid.clone()); - self.c.start_batch(batch).await - } - - /// Commit this batch. - /// - /// The Faktory server will not queue any callbacks, unless the batch is committed. - /// Committing an empty batch will make the server queue the callback(s) right away. - /// Once committed, the batch can still be re-opened with [open_batch](Client::open_batch), - /// and extra jobs can be added to it. - pub async fn commit(self) -> Result<(), Error> { - self.c.commit_batch(self.bid).await - } -} - -// Not documented, but existing de fakto and also mentioned in the official client -// https://github.com/contribsys/faktory/blob/main/client/batch.go#L17-L19 -/// State of a `callback` job of a [`Batch`]. -#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq)] -#[non_exhaustive] -pub enum CallbackState { - /// Not enqueued yet. - #[serde(rename = "")] - Pending, - /// Enqueued by the server, because the jobs belonging to this batch have finished executing. - /// If a callback has been consumed, it's status is still `Enqueued`. - /// If a callback has finished with failure, it's status remains `Enqueued`. - #[serde(rename = "1")] - Enqueued, - /// The enqueued callback job has been consumed and successfully executed. - #[serde(rename = "2")] - FinishedOk, -} - -impl std::fmt::Display for CallbackState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use CallbackState::*; - let s = match self { - Pending => "Pending", - Enqueued => "Enqueued", - FinishedOk => "FinishedOk", - }; - write!(f, "{}", s) - } -} - -/// Batch status retrieved from Faktory server. -#[derive(Deserialize, Debug)] -pub struct BatchStatus { - // Fields "bid", "created_at", "description", "total", "pending", and "failed" - // are described in the docs: https://github.com/contribsys/faktory/wiki/Ent-Batches#status - /// Id of this batch. - pub bid: String, - - /// Batch creation date and time. - pub created_at: DateTime, - - /// Batch description, if any. - pub description: Option, - - /// Number of jobs in this batch. - pub total: usize, - - /// Number of pending jobs. - pub pending: usize, - - /// Number of failed jobs. - pub failed: usize, - - // The official golang client also mentions "parent_bid', "complete_st", and "success_st": - // https://github.com/contribsys/faktory/blob/main/client/batch.go#L8-L22 - /// Id of the parent batch, provided this batch is a child ("nested") batch. - pub parent_bid: Option, - - /// State of the `complete` callback. - /// - /// See [with_complete_callback](struct.BatchBuilder.html#method.with_complete_callback). - #[serde(rename = "complete_st")] - pub complete_callback_state: CallbackState, - - /// State of the `success` callback. - /// - /// See [with_success_callback](struct.BatchBuilder.html#method.with_success_callback). - #[serde(rename = "success_st")] - pub success_callback_state: CallbackState, -} - -#[cfg(feature = "ent")] -#[cfg_attr(docsrs, doc(cfg(feature = "ent")))] -impl<'a> BatchStatus { - /// Open the batch for which this `BatchStatus` has been retrieved. - /// - /// See [`open_batch`](Client::open_batch). - pub async fn open( - &self, - prod: &'a mut Client, - ) -> Result>, Error> { - prod.open_batch(self.bid.clone()).await - } -} - #[cfg(test)] mod test { use std::str::FromStr; diff --git a/src/proto/batch/status.rs b/src/proto/batch/status.rs new file mode 100644 index 00000000..38931d8f --- /dev/null +++ b/src/proto/batch/status.rs @@ -0,0 +1,91 @@ +#[cfg(doc)] +use super::Batch; + +use super::BatchHandle; +use crate::{Client, Error}; +use chrono::{DateTime, Utc}; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; + +// Not documented, but existing de fakto and also mentioned in the official client +// https://github.com/contribsys/faktory/blob/main/client/batch.go#L17-L19 +/// State of a `callback` job of a [`Batch`]. +#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq)] +#[non_exhaustive] +pub enum CallbackState { + /// Not enqueued yet. + #[serde(rename = "")] + Pending, + /// Enqueued by the server, because the jobs belonging to this batch have finished executing. + /// If a callback has been consumed, it's status is still `Enqueued`. + /// If a callback has finished with failure, it's status remains `Enqueued`. + #[serde(rename = "1")] + Enqueued, + /// The enqueued callback job has been consumed and successfully executed. + #[serde(rename = "2")] + FinishedOk, +} + +impl std::fmt::Display for CallbackState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use CallbackState::*; + let s = match self { + Pending => "Pending", + Enqueued => "Enqueued", + FinishedOk => "FinishedOk", + }; + write!(f, "{}", s) + } +} + +/// Batch status retrieved from Faktory server. +#[derive(Deserialize, Debug)] +pub struct BatchStatus { + // Fields "bid", "created_at", "description", "total", "pending", and "failed" + // are described in the docs: https://github.com/contribsys/faktory/wiki/Ent-Batches#status + /// Id of this batch. + pub bid: String, + + /// Batch creation date and time. + pub created_at: DateTime, + + /// Batch description, if any. + pub description: Option, + + /// Number of jobs in this batch. + pub total: usize, + + /// Number of pending jobs. + pub pending: usize, + + /// Number of failed jobs. + pub failed: usize, + + // The official golang client also mentions "parent_bid', "complete_st", and "success_st": + // https://github.com/contribsys/faktory/blob/main/client/batch.go#L8-L22 + /// Id of the parent batch, provided this batch is a child ("nested") batch. + pub parent_bid: Option, + + /// State of the `complete` callback. + /// + /// See [with_complete_callback](struct.BatchBuilder.html#method.with_complete_callback). + #[serde(rename = "complete_st")] + pub complete_callback_state: CallbackState, + + /// State of the `success` callback. + /// + /// See [with_success_callback](struct.BatchBuilder.html#method.with_success_callback). + #[serde(rename = "success_st")] + pub success_callback_state: CallbackState, +} + +impl<'a> BatchStatus { + /// Open the batch for which this `BatchStatus` has been retrieved. + /// + /// See [`open_batch`](Client::open_batch). + pub async fn open( + &self, + prod: &'a mut Client, + ) -> Result>, Error> { + prod.open_batch(self.bid.clone()).await + } +} From b7560bae57a7bbc77d3aa8c23ccdcdb83beea49d Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Tue, 27 Feb 2024 11:57:17 +0500 Subject: [PATCH 019/129] Use BatchId for operaions with Batch --- src/lib.rs | 2 +- src/proto/batch/cmd.rs | 15 +++++++-------- src/proto/batch/handle.rs | 12 ++++++------ src/proto/batch/mod.rs | 6 +++--- src/proto/batch/status.rs | 7 ++++--- src/proto/client/ent.rs | 12 ++++++------ src/proto/client/mod.rs | 4 ++-- src/proto/mod.rs | 3 +++ src/proto/single/id.rs | 16 ++++++++++++++++ src/proto/single/mod.rs | 10 +++++++--- src/proto/single/resp.rs | 5 ++++- tests/real/enterprise.rs | 10 +++++----- 12 files changed, 64 insertions(+), 38 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5e765d30..5797c55f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,7 +82,7 @@ pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; /// Constructs only available with the enterprise version of Faktory. pub mod ent { pub use crate::proto::{ - Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, JobState, Progress, + Batch, BatchBuilder, BatchHandle, BatchId, BatchStatus, CallbackState, JobState, Progress, ProgressUpdate, ProgressUpdateBuilder, }; } diff --git a/src/proto/batch/cmd.rs b/src/proto/batch/cmd.rs index 106e0b8f..bd238736 100644 --- a/src/proto/batch/cmd.rs +++ b/src/proto/batch/cmd.rs @@ -1,6 +1,5 @@ -use crate::ent::Batch; -use crate::proto::single::FaktoryCommand; -use crate::Error; +use crate::error::Error; +use crate::proto::{single::FaktoryCommand, Batch, BatchId}; use tokio::io::AsyncWriteExt; #[async_trait::async_trait] @@ -15,8 +14,8 @@ impl FaktoryCommand for Batch { macro_rules! batch_cmd { ($structure:ident, $cmd:expr) => { - impl From for $structure { - fn from(value: String) -> Self { + impl From for $structure { + fn from(value: BatchId) -> Self { $structure(value) } } @@ -33,11 +32,11 @@ macro_rules! batch_cmd { }; } -pub struct CommitBatch(String); +pub struct CommitBatch(BatchId); batch_cmd!(CommitBatch, "COMMIT"); -pub struct GetBatchStatus(String); +pub struct GetBatchStatus(BatchId); batch_cmd!(GetBatchStatus, "STATUS"); -pub struct OpenBatch(String); +pub struct OpenBatch(BatchId); batch_cmd!(OpenBatch, "OPEN"); diff --git a/src/proto/batch/handle.rs b/src/proto/batch/handle.rs index 1a1f0327..b8550a94 100644 --- a/src/proto/batch/handle.rs +++ b/src/proto/batch/handle.rs @@ -1,20 +1,20 @@ -use super::Batch; -use crate::{Client, Error, Job}; +use crate::error::Error; +use crate::proto::{Batch, BatchId, Client, Job}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; /// Represents a newly started or re-opened batch of jobs. pub struct BatchHandle<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> { - bid: String, + bid: BatchId, c: &'a mut Client, } impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { /// ID issued by the Faktory server to this batch. - pub fn id(&self) -> &str { - self.bid.as_ref() + pub fn id(&self) -> &BatchId { + &self.bid } - pub(crate) fn new(bid: String, c: &mut Client) -> BatchHandle<'_, S> { + pub(crate) fn new(bid: BatchId, c: &mut Client) -> BatchHandle<'_, S> { BatchHandle { bid, c } } diff --git a/src/proto/batch/mod.rs b/src/proto/batch/mod.rs index 86c98bad..da5e656e 100644 --- a/src/proto/batch/mod.rs +++ b/src/proto/batch/mod.rs @@ -1,7 +1,7 @@ #[cfg(doc)] use crate::Client; -use crate::Job; +use crate::proto::{BatchId, Job}; use derive_builder::Builder; mod cmd; @@ -104,7 +104,7 @@ pub use status::{BatchStatus, CallbackState}; /// .with_complete_callback(cb_job); /// /// let mut b = cl.start_batch(b).await?; -/// let bid = b.id().to_string(); +/// let bid = b.id().to_owned(); /// b.add(job).await?; /// b.commit().await?; /// @@ -131,7 +131,7 @@ pub use status::{BatchStatus, CallbackState}; pub struct Batch { #[serde(skip_serializing_if = "Option::is_none")] #[builder(setter(skip))] - parent_bid: Option, + parent_bid: Option, /// Batch description for Faktory WEB UI. #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/proto/batch/status.rs b/src/proto/batch/status.rs index 38931d8f..9d52e38f 100644 --- a/src/proto/batch/status.rs +++ b/src/proto/batch/status.rs @@ -2,7 +2,8 @@ use super::Batch; use super::BatchHandle; -use crate::{Client, Error}; +use crate::error::Error; +use crate::proto::{BatchId, Client}; use chrono::{DateTime, Utc}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; @@ -43,7 +44,7 @@ pub struct BatchStatus { // Fields "bid", "created_at", "description", "total", "pending", and "failed" // are described in the docs: https://github.com/contribsys/faktory/wiki/Ent-Batches#status /// Id of this batch. - pub bid: String, + pub bid: BatchId, /// Batch creation date and time. pub created_at: DateTime, @@ -63,7 +64,7 @@ pub struct BatchStatus { // The official golang client also mentions "parent_bid', "complete_st", and "success_st": // https://github.com/contribsys/faktory/blob/main/client/batch.go#L8-L22 /// Id of the parent batch, provided this batch is a child ("nested") batch. - pub parent_bid: Option, + pub parent_bid: Option, /// State of the `complete` callback. /// diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index d9375dd6..e0876d09 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -3,7 +3,7 @@ use super::super::{ Track, }; use super::{Client, ReadToken}; -use crate::ent::{Batch, BatchHandle}; +use crate::ent::{Batch, BatchHandle, BatchId}; use crate::error::Error; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; @@ -21,7 +21,7 @@ impl Client { } /// Fetch information on a batch of jobs execution progress. - pub async fn get_batch_status(&mut self, bid: String) -> Result, Error> { + pub async fn get_batch_status(&mut self, bid: BatchId) -> Result, Error> { let cmd = GetBatchStatus::from(bid); self.issue(&cmd).await?.read_json().await } @@ -36,22 +36,22 @@ impl Client { /// /// This will not error if a batch with the provided `bid` does not exist, /// rather `Ok(None)` will be returned. - pub async fn open_batch(&mut self, bid: String) -> Result>, Error> { + pub async fn open_batch(&mut self, bid: BatchId) -> Result>, Error> { let bid = self.issue(&OpenBatch::from(bid)).await?.maybe_bid().await?; Ok(bid.map(|bid| BatchHandle::new(bid, self))) } - pub(crate) async fn commit_batch(&mut self, bid: String) -> Result<(), Error> { + pub(crate) async fn commit_batch(&mut self, bid: BatchId) -> Result<(), Error> { self.issue(&CommitBatch::from(bid)).await?.read_ok().await } } impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> ReadToken<'a, S> { - pub(crate) async fn read_bid(self) -> Result { + pub(crate) async fn read_bid(self) -> Result { single::read_bid(&mut self.0.stream).await } - pub(crate) async fn maybe_bid(self) -> Result, Error> { + pub(crate) async fn maybe_bid(self) -> Result, Error> { use crate::error; let bid_read_res = single::read_bid(&mut self.0.stream).await; diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index f3461c17..4bd2afd2 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -136,8 +136,8 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::Client; -/// let bid = String::from("W8qyVle9vXzUWQOg"); +/// use faktory::{Client, ent::BatchId}; +/// let bid = BatchId::from("W8qyVle9vXzUWQOg"); /// let mut cl = Client::connect(None).await?; /// if let Some(status) = cl.get_batch_status(bid).await? { /// println!("This batch created at {}", status.created_at); diff --git a/src/proto/mod.rs b/src/proto/mod.rs index acc98dab..ea23861b 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -17,6 +17,9 @@ pub(crate) mod utils; #[cfg(feature = "ent")] pub use self::single::ent::{JobState, Progress, ProgressUpdate, ProgressUpdateBuilder, Track}; +#[cfg(feature = "ent")] +pub use self::single::BatchId; + #[cfg(feature = "ent")] mod batch; #[cfg(feature = "ent")] diff --git a/src/proto/single/id.rs b/src/proto/single/id.rs index b5fc216f..fada2bfe 100644 --- a/src/proto/single/id.rs +++ b/src/proto/single/id.rs @@ -68,3 +68,19 @@ impl WorkerId { string_wrapper_impls!(WorkerId); // ----------------------------------------------------- + +/// Batch identifier. +/// +/// This is a wrapper over the string identifier issued by the Faktory server. +/// Only used for operations with [`Batch`](struct.Batch.html) in Enterprise Faktory. +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct BatchId(String); + +string_wrapper_impls!(BatchId); + +use serde_json::Value; +impl From for Value { + fn from(value: BatchId) -> Self { + value.0.into() + } +} diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index c3e1cfb3..1fa4669f 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -9,13 +9,17 @@ mod id; mod resp; mod utils; +pub use cmd::*; +pub use id::{JobId, WorkerId}; +pub use resp::*; + #[cfg(feature = "ent")] #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] pub mod ent; -pub use cmd::*; -pub use id::{JobId, WorkerId}; -pub use resp::*; +#[cfg(feature = "ent")] +#[cfg_attr(docsrs, doc(cfg(feature = "ent")))] +pub use id::BatchId; const JOB_DEFAULT_QUEUE: &str = "default"; const JOB_DEFAULT_RESERVED_FOR_SECS: usize = 600; diff --git a/src/proto/single/resp.rs b/src/proto/single/resp.rs index 095c5511..118f4547 100644 --- a/src/proto/single/resp.rs +++ b/src/proto/single/resp.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "ent")] +use crate::ent::BatchId; + use crate::error::{self, Error}; use tokio::io::{AsyncBufReadExt, AsyncReadExt}; @@ -62,7 +65,7 @@ pub async fn read_json(r: R) -> Result { +pub async fn read_bid(r: R) -> Result { match read(r).await? { RawResponse::Blob(ref b) if b.is_empty() => Err(error::Protocol::BadType { expected: "non-empty blob representation of batch id", diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index f2b70193..bffe0fb4 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -594,7 +594,7 @@ async fn test_batch_of_jobs_can_be_initiated() { let mut b = p.start_batch(batch).await.unwrap(); // let's remember batch id: - let bid = b.id().to_string(); + let bid = b.id().to_owned(); assert!(b.add(job_1).await.unwrap().is_none()); assert!(b.add(job_2).await.unwrap().is_none()); @@ -822,7 +822,7 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { ) .await .unwrap(); - let bid = b.id().to_string(); + let bid = b.id().to_owned(); // push 3 jobs onto this batch, but DO NOT commit the batch: for _ in 0..3 { @@ -989,7 +989,7 @@ async fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { .with_success_callback(callbacks.next().unwrap()); let mut b = p.start_batch(b).await.unwrap(); - let bid = b.id().to_string(); + let bid = b.id().to_owned(); b.add(jobs.next().unwrap()).await.unwrap(); // 1 job b.add(jobs.next().unwrap()).await.unwrap(); // 2 jobs @@ -1000,7 +1000,7 @@ async fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { // ############################## SUBTEST 0 ########################################## // Let's try to open/reopen a batch we have never declared: let b = p - .open_batch(String::from("non-existent-batch-id")) + .open_batch(BatchId::from("non-existent-batch-id")) .await .unwrap(); // The server will error back on this, with "No such batch ", but @@ -1067,7 +1067,7 @@ async fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { nested_callbacks.next().unwrap(), ); let nested_batch = b.start_batch(nested_batch_declaration).await.unwrap(); - let nested_bid = nested_batch.id().to_string(); + let nested_bid = nested_batch.id().to_owned(); // committing the nested batch without any jobs // since those are just not relevant for this test: nested_batch.commit().await.unwrap(); From 2fb7c632e1e578570939b968868c21774b1b066e Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Wed, 28 Feb 2024 11:59:03 +0500 Subject: [PATCH 020/129] re-export async trait --- src/lib.rs | 1 + src/worker/runner.rs | 10 +++++----- tests/tls.rs | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5797c55f..52d007b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,6 +76,7 @@ mod worker; pub use crate::error::Error; pub use crate::proto::{Client, Job, JobBuilder, JobId, Reconnect, WorkerId}; pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; +pub use async_trait; #[cfg(feature = "ent")] #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] diff --git a/src/worker/runner.rs b/src/worker/runner.rs index 71a6545c..63e0c0b7 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -1,9 +1,9 @@ -use crate::Job; -use std::future::Future; - #[cfg(doc)] use super::Worker; +use crate::Job; +use std::future::Future; + /// Implementations of this trait can be registered to run jobs in a [`Worker`](Worker). /// /// # Example @@ -14,14 +14,14 @@ use super::Worker; /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{WorkerBuilder, JobRunner, Job}; +/// use faktory::{async_trait::async_trait, Job, JobRunner, WorkerBuilder}; /// use std::io; /// /// struct MyHandler { /// config: String, /// } /// -/// #[async_trait::async_trait] +/// #[async_trait] /// impl JobRunner for MyHandler { /// type Error = io::Error; /// async fn run(&self, job: Job) -> Result<(), Self::Error> { diff --git a/tests/tls.rs b/tests/tls.rs index 13f7bc8f..d7943018 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -72,7 +72,7 @@ mod fixtures { pub use tls::TestServerCertVerifier; mod handler { - use faktory::*; + use faktory::{async_trait::async_trait, Job, JobRunner}; use std::{ io, @@ -100,7 +100,7 @@ mod fixtures { } } - #[async_trait::async_trait] + #[async_trait] impl JobRunner for JobHandler { type Error = io::Error; From 755d4523aaaf57dc3ff692463807a8da53443e9d Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Wed, 28 Feb 2024 12:03:35 +0500 Subject: [PATCH 021/129] Make 'jid', 'kind' and 'args' public on Job struct --- src/proto/single/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index 1fa4669f..815c6a12 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -71,7 +71,7 @@ const JOB_DEFAULT_BACKTRACE: usize = 0; pub struct Job { /// The job's unique identifier. #[builder(default = "JobId::random()")] - pub(crate) jid: JobId, + pub jid: JobId, /// The queue this job belongs to. Usually `default`. #[builder(default = "JOB_DEFAULT_QUEUE.into()")] @@ -80,11 +80,11 @@ pub struct Job { /// The job's type. Called `kind` because `type` is reserved. #[serde(rename = "jobtype")] #[builder(setter(custom))] - pub(crate) kind: String, + pub kind: String, /// The arguments provided for this job. #[builder(setter(custom), default = "Vec::new()")] - pub(crate) args: Vec, + pub args: Vec, /// When this job was created. // note that serializing works correctly here since the default chrono serialization From 65110cf69855cf5d1d835b375ce2d3c9e6fb7e73 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Fri, 1 Mar 2024 08:24:26 +0500 Subject: [PATCH 022/129] Clean up in worker crate --- src/worker/mod.rs | 24 ++++++------- src/worker/registries.rs | 77 ---------------------------------------- src/worker/state.rs | 70 ++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 89 deletions(-) delete mode 100644 src/worker/registries.rs create mode 100644 src/worker/state.rs diff --git a/src/worker/mod.rs b/src/worker/mod.rs index 16b52888..0a4ce0c0 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -1,8 +1,7 @@ use super::proto::{Client, Reconnect}; -use crate::{ - proto::{Ack, Fail}, - Error, Job, JobId, -}; +use crate::error::Error; +use crate::proto::{Ack, Fail, Job, JobId}; +use fnv::FnvHashMap; use std::sync::{atomic, Arc}; use std::{error::Error as StdError, sync::atomic::AtomicUsize}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; @@ -10,17 +9,18 @@ use tokio::task::JoinHandle; mod builder; mod health; -mod registries; mod runner; +mod state; pub use builder::WorkerBuilder; -use registries::{CallbacksRegistry, StatesRegistry}; pub use runner::JobRunner; pub(crate) const STATUS_RUNNING: usize = 0; pub(crate) const STATUS_QUIET: usize = 1; pub(crate) const STATUS_TERMINATING: usize = 2; +type CallbacksRegistry = FnvHashMap>; + /// `Worker` is used to run a worker that processes jobs provided by Faktory. /// /// # Building the worker @@ -138,7 +138,7 @@ pub(crate) const STATUS_TERMINATING: usize = 2; /// pub struct Worker { c: Client, - worker_states: Arc, + worker_states: Arc, callbacks: Arc>, terminated: bool, } @@ -154,7 +154,7 @@ impl Worker { Worker { c, callbacks: Arc::new(callbacks), - worker_states: Arc::new(StatesRegistry::new(workers_count)), + worker_states: Arc::new(state::WorkerStatesRegistry::new(workers_count)), terminated: false, } } @@ -190,7 +190,7 @@ impl self.c.issue(&Ack::from(jid)).await, Err(ref fail) => self.c.issue(fail).await, @@ -199,7 +199,7 @@ impl r, Err(e) => { - wstate.last_job_result = Some(res); + wstate.save_last_result(res); return Err(e); } }; @@ -210,7 +210,7 @@ impl usize { let mut running = 0; for wstate in self.worker_states.iter() { - let may_be_jid = wstate.lock().unwrap().running_job.take(); + let may_be_jid = wstate.lock().unwrap().take_cuurently_running(); if let Some(jid) = may_be_jid { running += 1; let f = Fail::new(jid, "unknown", "terminated"); diff --git a/src/worker/registries.rs b/src/worker/registries.rs deleted file mode 100644 index a10cebe2..00000000 --- a/src/worker/registries.rs +++ /dev/null @@ -1,77 +0,0 @@ -use super::runner::BoxedJobRunner; -use crate::proto::{Fail, JobId}; -use fnv::FnvHashMap; -use std::{ - ops::{Deref, DerefMut}, - sync::Mutex, -}; - -// --------------- CALLBACKS (Job Handlers) ---------------- - -pub(crate) struct CallbacksRegistry(FnvHashMap>); - -impl Deref for CallbacksRegistry { - type Target = FnvHashMap>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for CallbacksRegistry { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Default for CallbacksRegistry { - fn default() -> CallbacksRegistry { - Self(FnvHashMap::default()) - } -} - -// -------------------- WORKER STATES --------------------- - -#[derive(Default)] -pub(crate) struct WorkerState { - pub(crate) last_job_result: Option>, - pub(crate) running_job: Option, -} - -pub(crate) struct StatesRegistry(Vec>); - -impl Deref for StatesRegistry { - type Target = Vec>; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for StatesRegistry { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl StatesRegistry { - pub(crate) fn new(workers_count: usize) -> Self { - Self((0..workers_count).map(|_| Default::default()).collect()) - } - - pub(crate) fn register_running(&self, worker: usize, jid: JobId) { - self[worker].lock().expect("lock acquired").running_job = Some(jid); - } - - pub(crate) fn register_success(&self, worker: usize, jid: JobId) { - self[worker].lock().expect("lock acquired").last_job_result = Some(Ok(jid)); - } - - pub(crate) fn register_failure(&self, worker: usize, f: &Fail) { - self[worker].lock().expect("lock acquired").last_job_result = Some(Err(f.clone())); - } - - pub(crate) fn reset(&self, worker: usize) { - let mut state = self[worker].lock().expect("lock acquired"); - state.last_job_result = None; - state.running_job = None; - } -} diff --git a/src/worker/state.rs b/src/worker/state.rs new file mode 100644 index 00000000..0fb9fe0b --- /dev/null +++ b/src/worker/state.rs @@ -0,0 +1,70 @@ +use crate::proto::{Fail, JobId}; +use std::{ + ops::{Deref, DerefMut}, + sync::Mutex, +}; + +#[derive(Default)] +pub(crate) struct WorkerState { + last_job_result: Option>, + running_job: Option, +} + +impl WorkerState { + pub(crate) fn take_last_result(&mut self) -> Option> { + self.last_job_result.take() + } + + pub(crate) fn take_cuurently_running(&mut self) -> Option { + self.running_job.take() + } + + pub(crate) fn save_last_result(&mut self, res: Result) { + self.last_job_result = Some(res) + } +} + +pub(crate) struct WorkerStatesRegistry(Vec>); + +impl Deref for WorkerStatesRegistry { + type Target = Vec>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for WorkerStatesRegistry { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl WorkerStatesRegistry { + pub(crate) fn new(workers_count: usize) -> Self { + Self((0..workers_count).map(|_| Default::default()).collect()) + } + + pub(crate) fn register_running(&self, worker: usize, jid: JobId) { + self[worker].lock().expect("lock acquired").running_job = Some(jid); + } + + pub(crate) fn register_success(&self, worker: usize, jid: JobId) { + self[worker] + .lock() + .expect("lock acquired") + .save_last_result(Ok(jid)); + } + + pub(crate) fn register_failure(&self, worker: usize, f: &Fail) { + self[worker] + .lock() + .expect("lock acquired") + .save_last_result(Err(f.clone())); + } + + pub(crate) fn reset(&self, worker: usize) { + let mut state = self[worker].lock().expect("lock acquired"); + state.last_job_result = None; + state.running_job = None; + } +} From dcd6d9746cac2d93769091082abecd9bc1964d68 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Fri, 1 Mar 2024 08:31:32 +0500 Subject: [PATCH 023/129] Re-export rustls for convenience --- src/lib.rs | 5 ++++- tests/tls.rs | 5 ++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 52d007b2..f8b1c4eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,9 +89,12 @@ pub mod ent { } #[cfg(feature = "tls")] -#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] mod tls; #[cfg(feature = "tls")] #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] pub use tls::TlsStream; + +#[cfg(feature = "tls")] +#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] +pub use tokio_rustls::rustls; diff --git a/tests/tls.rs b/tests/tls.rs index d7943018..e98763c6 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -1,13 +1,12 @@ #![cfg(feature = "tls")] -use faktory::*; +use faktory::rustls::{ClientConfig, SignatureScheme}; +use faktory::{Client, Job, TlsStream, WorkerBuilder}; use serde_json::Value; use std::{ env, sync::{self, Arc}, }; -use tokio_rustls::rustls::ClientConfig; -use tokio_rustls::rustls::SignatureScheme; #[tokio::test(flavor = "multi_thread")] async fn roundtrip_tls() { From 3c9ade4dfaf6972708bb073677629adc9f2fed1a Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Fri, 1 Mar 2024 10:18:13 +0500 Subject: [PATCH 024/129] re-export tokio main, clean up --- Cargo.lock | 7 +++---- Cargo.toml | 2 -- src/bin/loadtest.rs | 2 +- src/lib.rs | 1 + 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c1dd7f2..72b7b905 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -403,7 +403,6 @@ dependencies = [ "derive_builder", "fnv", "hostname", - "libc", "num-bigint", "oid-registry", "pin-project", @@ -878,12 +877,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 256a7e26..1f621d7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,6 @@ chrono = { version = "0.4", features = [ derive_builder = "0.12.0" fnv = "1.0.5" hostname = "0.3" -libc = "0.2" pin-project = { version = "1.1.4", optional = true } rand = "0.8" serde = "1.0" @@ -40,7 +39,6 @@ thiserror = "1.0.30" tokio = { version = "1.35.1", features = [ "io-util", "net", - "rt", "time", "macros", "rt-multi-thread", diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index 095912ba..13a92f74 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -9,7 +9,7 @@ use std::time; const QUEUES: &[&str] = &["queue0", "queue1", "queue2", "queue3", "queue4"]; -#[tokio::main] +#[faktory::main] async fn main() { let matches = Command::new("My Super Program (Async)") .version("0.1") diff --git a/src/lib.rs b/src/lib.rs index f8b1c4eb..72a17d5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,6 +77,7 @@ pub use crate::error::Error; pub use crate::proto::{Client, Job, JobBuilder, JobId, Reconnect, WorkerId}; pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; pub use async_trait; +pub use tokio::main; #[cfg(feature = "ent")] #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] From 55890c8aec2c6b3606506c819836f934f4d54312 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sat, 2 Mar 2024 13:56:19 +0500 Subject: [PATCH 025/129] Group re-exports in lib.rs --- src/lib.rs | 5 +++-- src/worker/runner.rs | 2 +- tests/tls.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 72a17d5f..914b8d92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,8 +76,6 @@ mod worker; pub use crate::error::Error; pub use crate::proto::{Client, Job, JobBuilder, JobId, Reconnect, WorkerId}; pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; -pub use async_trait; -pub use tokio::main; #[cfg(feature = "ent")] #[cfg_attr(docsrs, doc(cfg(feature = "ent")))] @@ -96,6 +94,9 @@ mod tls; #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] pub use tls::TlsStream; +// 3rd-party libs re-exports +pub use async_trait::async_trait; +pub use tokio::main; #[cfg(feature = "tls")] #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] pub use tokio_rustls::rustls; diff --git a/src/worker/runner.rs b/src/worker/runner.rs index 63e0c0b7..98488d90 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -14,7 +14,7 @@ use std::future::Future; /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{async_trait::async_trait, Job, JobRunner, WorkerBuilder}; +/// use faktory::{async_trait, Job, JobRunner, WorkerBuilder}; /// use std::io; /// /// struct MyHandler { diff --git a/tests/tls.rs b/tests/tls.rs index e98763c6..c299e3b2 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -71,7 +71,7 @@ mod fixtures { pub use tls::TestServerCertVerifier; mod handler { - use faktory::{async_trait::async_trait, Job, JobRunner}; + use faktory::{async_trait, Job, JobRunner}; use std::{ io, From 00a6d1dc02f421f8cdc640bccad65a36b46a1f39 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Sat, 2 Mar 2024 14:21:03 +0500 Subject: [PATCH 026/129] Fix copypasta in cmd.rs --- src/proto/single/cmd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index 03e3cdc6..11453d35 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -252,7 +252,7 @@ impl FaktoryCommand for Push { } } -// ---------------------- QUEUE ------------------- +// ---------------------- PUSHB ------------------- pub struct PushBulk(Vec); @@ -272,7 +272,7 @@ impl FaktoryCommand for PushBulk { } } -// ---------------------------------------------- +// ---------------------- QUEUE ------------------- pub enum QueueAction { Pause, From 5382d5e72ec2ec7c4005b307b966efefa7c648f1 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Wed, 20 Mar 2024 13:08:48 +0500 Subject: [PATCH 027/129] Rm redundant method on Worker --- src/worker/builder.rs | 9 +-------- tests/real/community.rs | 2 +- tests/real/enterprise.rs | 6 +++--- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/worker/builder.rs b/src/worker/builder.rs index 7b95ef95..fb81ebdb 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -16,15 +16,8 @@ pub struct WorkerBuilder { callbacks: CallbacksRegistry, } -impl WorkerBuilder { - /// Create a builder for asynchronous version of `Worker`. - pub fn default_async() -> WorkerBuilder { - WorkerBuilder::default() - } -} - impl Default for WorkerBuilder { - /// Create a builder for asynchronous version of `ConWorkersumer`. + /// Create a builder for asynchronous version of `Worker`. /// /// See [`WorkerBuilder`](struct.WorkerBuilder.html) fn default() -> Self { diff --git a/tests/real/community.rs b/tests/real/community.rs index 6854efd0..dabf1ab4 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -61,7 +61,7 @@ async fn multi() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); - let mut c = WorkerBuilder::default_async(); + let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(local.into()); c.register(local, move |j| { diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index bffe0fb4..6342f6bd 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -209,7 +209,7 @@ async fn ent_unique_job_until_success() { // will sleep for a corresponding period of time, pretending // to work hard: let mut client_a = Client::connect(Some(&url1)).await.unwrap(); - let mut worker_a = WorkerBuilder::default_async(); + let mut worker_a = WorkerBuilder::default(); worker_a.register(job_type, |job| async move { let args = job.args().to_owned(); let mut args = args.iter(); @@ -289,7 +289,7 @@ async fn ent_unique_job_until_start() { let url1 = url.clone(); let handle = tokio::spawn(async move { let mut client_a = Client::connect(Some(&url1)).await.unwrap(); - let mut worker_a = WorkerBuilder::default_async(); + let mut worker_a = WorkerBuilder::default(); worker_a.register(job_type, |job| async move { let args = job.args().to_owned(); let mut args = args.iter(); @@ -379,7 +379,7 @@ async fn ent_unique_job_bypass_unique_lock() { // let's consume three times from the queue to verify that the first two jobs // have been enqueued for real, while the last one has not. - let mut c = WorkerBuilder::default_async(); + let mut c = WorkerBuilder::default(); c.register("order", print_job); let mut c = c.connect(Some(&url)).await.unwrap(); From dde77ceb7043092e6926de8eef0f464b4436244e Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Wed, 20 Mar 2024 13:30:32 +0500 Subject: [PATCH 028/129] Respecrt clippy on beta chan --- src/proto/client/mod.rs | 4 ++-- src/proto/single/resp.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 4bd2afd2..fbdd1dc8 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -260,7 +260,7 @@ where hello.hostname = Some(self.opts.hostname.clone().unwrap()); hello.wid = Some(self.opts.wid.clone().unwrap()); hello.pid = Some(self.opts.pid.unwrap()); - hello.labels = self.opts.labels.clone(); + hello.labels.clone_from(&self.opts.labels); } if hi.salt.is_some() { @@ -362,7 +362,7 @@ where } /// Resume the given queues. - pub async fn queue_resume>(&mut self, queues: &[Q]) -> Result<(), Error> + pub async fn queue_resume(&mut self, queues: &[Q]) -> Result<(), Error> where Q: AsRef + Sync, { diff --git a/src/proto/single/resp.rs b/src/proto/single/resp.rs index 118f4547..18e5a9b1 100644 --- a/src/proto/single/resp.rs +++ b/src/proto/single/resp.rs @@ -257,7 +257,7 @@ mod test { use super::{read, RawResponse}; use crate::error::{self, Error}; - use serde_json::{self, Map, Value}; + use serde_json::{Map, Value}; use std::io::Cursor; use tokio::io::AsyncBufReadExt; From faeb4369702f3ab6c9ed8fd601ff4f61c1977c8a Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Wed, 20 Mar 2024 13:40:39 +0500 Subject: [PATCH 029/129] Use try_seconds instead of seconds on Duration struct --- src/proto/single/ent/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proto/single/ent/mod.rs b/src/proto/single/ent/mod.rs index e6f4f63e..7961f8b0 100644 --- a/src/proto/single/ent/mod.rs +++ b/src/proto/single/ent/mod.rs @@ -100,7 +100,7 @@ mod test { #[test] fn test_expiration_feature_for_enterprise_faktory() { - let five_min = chrono::Duration::seconds(299); + let five_min = chrono::Duration::try_seconds(299).unwrap(); let exp_at = Utc::now() + five_min; let job0 = half_stuff().expires_at(exp_at).build(); let stored = job0.custom.get("expires_at").unwrap(); @@ -126,8 +126,8 @@ mod test { #[test] fn test_same_purpose_setters_applied_simultaneously() { - let expires_at0 = Utc::now() + chrono::Duration::seconds(300); - let expires_at1 = Utc::now() + chrono::Duration::seconds(300); + let expires_at0 = Utc::now() + chrono::Duration::try_seconds(300).unwrap(); + let expires_at1 = Utc::now() + chrono::Duration::try_seconds(300).unwrap(); let job = half_stuff() .unique_for(59) .add_to_custom_data("unique_for", 599) From 9ba6e855f4d3f75712345d6bbd5f21ebf42aa431 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Wed, 20 Mar 2024 13:44:35 +0500 Subject: [PATCH 030/129] Use try_seconds instead of seconds on Duration struct in tests --- tests/real/enterprise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index 6342f6bd..26381d73 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -51,7 +51,7 @@ async fn ent_expiring_job() { // prepare an expiring job: let job_ttl_secs: u64 = 3; - let ttl = chrono::Duration::seconds(job_ttl_secs as i64); + let ttl = chrono::Duration::try_seconds(job_ttl_secs as i64).unwrap(); let job1 = JobBuilder::new("AnExpiringJob") .args(vec!["ISBN-13:9781718501850"]) .queue(local) From fe65c67a00ef61e4513f0724d52be4a9f5efe5c0 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Wed, 20 Mar 2024 14:26:04 +0500 Subject: [PATCH 031/129] Pin chrono at 0.4.32 to make minimal-versions pass --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 1f621d7f..f78b78e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ x509-parser = "0.15.1" rustls = "0.22.1" num-bigint = "0.4.2" oid-registry = "0.6.1" +chrono = "0.4.32" [[bin]] name = "loadtest" From ab59c9762af6ff5951d936e4b642c5195c567cfa Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Wed, 20 Mar 2024 14:30:03 +0500 Subject: [PATCH 032/129] Pin chrono at 0.4.32 to make minimal-versions pass --- Cargo.lock | 172 +++++++++++++++++++++++++++-------------------------- 1 file changed, 87 insertions(+), 85 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72b7b905..b5879a7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,9 +34,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -138,18 +138,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", ] [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", ] [[package]] @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.1" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c764d619ca78fccbf3069b37bd7af92577f044bb15236036662d79b6559f25b7" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bytes" @@ -196,9 +196,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.86" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-if" @@ -208,31 +208,33 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", - "windows-targets 0.52.0", + "wasm-bindgen", + "windows-targets 0.52.4", ] [[package]] name = "clap" -version = "4.5.1" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" +checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -390,7 +392,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", ] [[package]] @@ -470,9 +472,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hostname" @@ -532,9 +534,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -553,9 +555,9 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "match_cfg" @@ -586,9 +588,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", @@ -682,22 +684,22 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", ] [[package]] @@ -720,9 +722,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -812,9 +814,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" +checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" [[package]] name = "rustls-webpki" @@ -850,7 +852,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", ] [[package]] @@ -922,9 +924,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.50" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -945,22 +947,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", ] [[package]] @@ -1034,7 +1036,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", ] [[package]] @@ -1050,9 +1052,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -1061,9 +1063,9 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" dependencies = [ "async-stream", "bytes", @@ -1142,9 +1144,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1152,24 +1154,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1177,22 +1179,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "winapi" @@ -1222,7 +1224,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -1240,7 +1242,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -1260,17 +1262,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -1281,9 +1283,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -1293,9 +1295,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -1305,9 +1307,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -1317,9 +1319,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -1329,9 +1331,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -1341,9 +1343,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -1353,9 +1355,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "x509-parser" From 8b979a4f784a7a7d82d47f91de6aa9d65b9c3348 Mon Sep 17 00:00:00 2001 From: Pavel Mikhalkevich Date: Thu, 21 Mar 2024 01:02:36 +0500 Subject: [PATCH 033/129] Fix opts in Worker::connect method --- src/worker/builder.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/worker/builder.rs b/src/worker/builder.rs index fb81ebdb..eba846b5 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -107,11 +107,12 @@ impl WorkerBuilder { /// /// See [`connect`](WorkerBuilder::connect). pub async fn connect( - self, + mut self, url: Option<&str>, ) -> Result, E>, Error> { let url = utils::parse_provided_or_from_env(url)?; let stream = TokioStream::connect(utils::host_from_url(&url)).await?; + self.opts.is_worker = true; let buffered = BufStream::new(stream); let client = Client::new(buffered, self.opts).await?; Ok(Worker::new(client, self.workers_count, self.callbacks).await) From 5077f2a83721b4de257abdfbf493d06f088cc021 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 2 Apr 2024 21:11:02 +0500 Subject: [PATCH 034/129] Use 'dep:' for feature deps --- Cargo.toml | 5 ++--- Makefile | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f78b78e7..56ea91ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ exclude = [".github", "docker", ".gitignore", "Makefile"] [features] default = [] -tls = ["tokio-rustls", "pin-project"] +tls = ["dep:tokio-rustls", "tokio/net", "tokio/io-util", "dep:pin-project"] binaries = ["clap"] ent = [] @@ -37,8 +37,7 @@ serde_json = "1.0" sha2 = "0.10.0" thiserror = "1.0.30" tokio = { version = "1.35.1", features = [ - "io-util", - "net", + "rt", "time", "macros", "rt-multi-thread", diff --git a/Makefile b/Makefile index 6e23d9c0..35cac22a 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +FAKTORY_IP=127.0.0.1 FAKTORY_HOST=localhost FAKTORY_PORT=7419 FAKTORY_PORT_SECURE=17419 @@ -17,8 +18,8 @@ doc: faktory: docker run --rm -d \ -v faktory-data:/var/lib/faktory \ - -p ${FAKTORY_HOST}:${FAKTORY_PORT}:7419 \ - -p ${FAKTORY_HOST}:${FAKTORY_PORT_UI}:7420 \ + -p ${FAKTORY_IP}:${FAKTORY_PORT}:7419 \ + -p ${FAKTORY_IP}:${FAKTORY_PORT_UI}:7420 \ --name faktory \ contribsys/faktory:latest \ /faktory -b :7419 -w :7420 From 5b041c92d38176350de0b614cb679502989de279 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 3 Apr 2024 01:09:49 +0500 Subject: [PATCH 035/129] Use tokio-native-tls for tls feature --- Cargo.lock | 201 +++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 11 ++- src/error.rs | 6 ++ src/lib.rs | 3 - src/tls.rs | 77 +++++++++----------- tests/tls.rs | 103 +++----------------------- 6 files changed, 252 insertions(+), 149 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b5879a7b..9f5a807d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,18 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + [[package]] name = "block-buffer" version = "0.10.4" @@ -254,6 +266,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -395,6 +417,16 @@ dependencies = [ "syn 2.0.53", ] +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "faktory" version = "0.12.5" @@ -416,18 +448,39 @@ dependencies = [ "sha2", "thiserror", "tokio", - "tokio-rustls", + "tokio-native-tls", "tokio-test", "url", "x509-parser", ] +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -553,6 +606,12 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + [[package]] name = "log" version = "0.4.21" @@ -597,6 +656,24 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -676,6 +753,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.53", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -708,6 +829,12 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "powerfmt" version = "0.2.0" @@ -798,6 +925,19 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.22.2" @@ -835,6 +975,38 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.197" @@ -945,6 +1117,18 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "thiserror" version = "1.0.58" @@ -1040,13 +1224,12 @@ dependencies = [ ] [[package]] -name = "tokio-rustls" -version = "0.25.0" +name = "tokio-native-tls" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ - "rustls", - "rustls-pki-types", + "native-tls", "tokio", ] @@ -1130,6 +1313,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 56ea91ba..031103fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ exclude = [".github", "docker", ".gitignore", "Makefile"] [features] default = [] -tls = ["dep:tokio-rustls", "tokio/net", "tokio/io-util", "dep:pin-project"] +tls = ["dep:tokio-native-tls", "dep:pin-project"] binaries = ["clap"] ent = [] @@ -37,12 +37,15 @@ serde_json = "1.0" sha2 = "0.10.0" thiserror = "1.0.30" tokio = { version = "1.35.1", features = [ - "rt", - "time", + "io-util", "macros", + "net", + "rt", "rt-multi-thread", + "time", + ] } -tokio-rustls = { version = "0.25.0", optional = true } +tokio-native-tls = { version = "0.3.1", optional = true } url = "2" [dev-dependencies] diff --git a/src/error.rs b/src/error.rs index 417ec6eb..bc5e0a7e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,6 +41,12 @@ pub enum Error { /// These generally indicate a mismatch between what the client expects and what the server provided. #[error("serialization")] Serialization(#[source] serde_json::Error), + + /// Indicates an error in the underlying TLS stream. + #[cfg(feature = "tls")] + #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] + #[error("underlying tls stream")] + TlsStream(#[source] tokio_native_tls::native_tls::Error), } /// Errors specific to connection logic. diff --git a/src/lib.rs b/src/lib.rs index 914b8d92..79538edb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,6 +97,3 @@ pub use tls::TlsStream; // 3rd-party libs re-exports pub use async_trait::async_trait; pub use tokio::main; -#[cfg(feature = "tls")] -#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] -pub use tokio_rustls::rustls; diff --git a/src/tls.rs b/src/tls.rs index b6126df7..2a80c690 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -5,14 +5,12 @@ use crate::{proto::utils, Error, Reconnect}; use std::fmt::Debug; use std::io; use std::ops::{Deref, DerefMut}; -use std::sync::Arc; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::TcpStream as TokioTcpStream; -use tokio_rustls::client::TlsStream as UnderlyingTlsStream; -use tokio_rustls::rustls::{ClientConfig, RootCertStore}; -use tokio_rustls::TlsConnector; +use tokio_native_tls::TlsStream as NativeTlsStream; +use tokio_native_tls::{native_tls::TlsConnector, TlsConnector as AsyncTlsConnector}; -/// A reconnectable asynchronous stream encrypted with TLS. +/// A reconnectable stream encrypted with TLS. /// /// This can be used as an argument to [`WorkerBuilder::connect_with`] and [`Client::connect_with`] to /// connect to a TLS-secured Faktory server. @@ -30,10 +28,10 @@ use tokio_rustls::TlsConnector; /// #[pin_project::pin_project] pub struct TlsStream { - connector: TlsConnector, - hostname: &'static str, + connector: AsyncTlsConnector, + hostname: String, #[pin] - stream: UnderlyingTlsStream, + stream: NativeTlsStream, } impl TlsStream { @@ -50,28 +48,15 @@ impl TlsStream { /// ``` /// /// If `url` is given, but does not specify a port, it defaults to 7419. - /// - /// Internally creates a `ClientConfig` with an empty root certificates store and no client - /// authentication. Use [`with_client_config`](TlsStream::with_client_config) - /// or [`with_connector`](TlsStream::with_connector) for customized - /// `ClientConfig` and `TlsConnector` accordingly. pub async fn connect(url: Option<&str>) -> Result { - let conf = ClientConfig::builder() - .with_root_certificates(RootCertStore::empty()) - .with_no_client_auth(); - let con = TlsConnector::from(Arc::new(conf)); - TlsStream::with_connector(con, url).await + TlsStream::with_connector( + TlsConnector::builder().build().map_err(Error::TlsStream)?, + url, + ) + .await } - /// Create a new asynchronous TLS connection over TCP using a non-default TLS configuration. - /// - /// See `connect` for details about the `url` parameter. - pub async fn with_client_config(conf: ClientConfig, url: Option<&str>) -> Result { - let con = TlsConnector::from(Arc::new(conf)); - TlsStream::with_connector(con, url).await - } - - /// Create a new asynchronous TLS connection over TCP using a connector with a non-default TLS configuration. + /// Create a new TLS connection over TCP using a non-default TLS configuration. /// /// See `connect` for details about the `url` parameter. pub async fn with_connector(connector: TlsConnector, url: Option<&str>) -> Result { @@ -81,8 +66,7 @@ impl TlsStream { }?; let hostname = utils::host_from_url(&url); let tcp_stream = TokioTcpStream::connect(&hostname).await?; - let hostname: &'static str = url.host_str().unwrap().to_string().leak(); - Ok(TlsStream::new(tcp_stream, connector, hostname).await?) + Ok(TlsStream::new(tcp_stream, connector, &hostname).await?) } } @@ -90,33 +74,34 @@ impl TlsStream where S: AsyncRead + AsyncWrite + Send + Unpin + Reconnect + Debug + 'static, { - /// Create a new asynchronous TLS connection on an existing stream. + /// Create a new TLS connection on an existing stream. /// /// Internally creates a `ClientConfig` with an empty root certificates store and no client /// authentication. Use [`new`](TlsStream::new) for a customized `TlsConnector`. - pub async fn default(stream: S, hostname: &'static str) -> io::Result { - let conf = ClientConfig::builder() - .with_root_certificates(RootCertStore::empty()) - .with_no_client_auth(); - - Self::new(stream, TlsConnector::from(Arc::new(conf)), hostname).await + /// Create a new TLS connection on an existing stream. + pub async fn default(stream: S, hostname: &str) -> io::Result { + let connector = TlsConnector::builder() + .build() + .map_err(Error::TlsStream) + .unwrap(); + Self::new(stream, connector, hostname).await } - /// Create a new asynchronous TLS connection on an existing stream with a non-default TLS configuration. + /// Create a new TLS connection on an existing stream with a non-default TLS configuration. pub async fn new( stream: S, - connector: TlsConnector, - hostname: &'static str, + connector: impl Into, + hostname: &str, ) -> io::Result { - // let hostname: &'static str = hostname.to_string().leak(); let domain = hostname.try_into().expect("a valid DNS name or IP address"); + let connector = connector.into(); let tls_stream = connector .connect(domain, stream) .await .map_err(|e| io::Error::new(io::ErrorKind::ConnectionAborted, e))?; Ok(TlsStream { connector, - hostname, + hostname: hostname.into(), stream: tls_stream, }) } @@ -128,13 +113,19 @@ where S: AsyncRead + AsyncWrite + Send + Unpin + Reconnect + Debug + 'static + Sync, { async fn reconnect(&mut self) -> io::Result { - let stream = self.stream.get_mut().0.reconnect().await?; + let stream = self + .stream + .get_mut() + .get_mut() + .get_mut() + .reconnect() + .await?; Self::new(stream, self.connector.clone(), &self.hostname).await } } impl Deref for TlsStream { - type Target = UnderlyingTlsStream; + type Target = NativeTlsStream; fn deref(&self) -> &Self::Target { &self.stream } diff --git a/tests/tls.rs b/tests/tls.rs index c299e3b2..e9dda2d7 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -1,15 +1,13 @@ #![cfg(feature = "tls")] -use faktory::rustls::{ClientConfig, SignatureScheme}; use faktory::{Client, Job, TlsStream, WorkerBuilder}; use serde_json::Value; -use std::{ - env, - sync::{self, Arc}, -}; +use std::{env, sync}; #[tokio::test(flavor = "multi_thread")] async fn roundtrip_tls() { + use tokio_native_tls::native_tls::TlsConnector; + // We are utilizing the fact that the "FAKTORY_URL_SECURE" environment variable is set // as an indicator that the integration test can and should be performed. // @@ -32,25 +30,13 @@ async fn roundtrip_tls() { c.register_runner(local, fixtures::JobHandler::new(tx)); let tls = || async { - let verifier = fixtures::TestServerCertVerifier::new( - SignatureScheme::RSA_PSS_SHA512, - env::current_dir() - .unwrap() - .join("docker") - .join("certs") - .join("faktory.local.crt"), - ); - let client_config = ClientConfig::builder() - .dangerous() - .with_custom_certificate_verifier(Arc::new(verifier)) - .with_no_client_auth(); - - TlsStream::with_client_config( - client_config, - Some(&env::var("FAKTORY_URL_SECURE").unwrap()), - ) - .await - .unwrap() + let connector = TlsConnector::builder() + .danger_accept_invalid_certs(true) + .build() + .unwrap(); + TlsStream::with_connector(connector, Some(&env::var("FAKTORY_URL_SECURE").unwrap())) + .await + .unwrap() }; let mut c = c.connect_with(tls().await, None).await.unwrap(); @@ -68,11 +54,9 @@ async fn roundtrip_tls() { mod fixtures { pub use handler::JobHandler; - pub use tls::TestServerCertVerifier; mod handler { use faktory::{async_trait, Job, JobRunner}; - use std::{ io, sync::{mpsc::Sender, Arc, Mutex}, @@ -109,71 +93,4 @@ mod fixtures { } } } - - mod tls { - #![allow(unused_variables)] - - use std::fs; - use std::path::PathBuf; - - use tokio_rustls::rustls::client::danger::{ - HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, - }; - use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime}; - use tokio_rustls::rustls::DigitallySignedStruct; - use tokio_rustls::rustls::Error as RustlsError; - use tokio_rustls::rustls::SignatureScheme; - use x509_parser::pem::parse_x509_pem; - - #[derive(Debug)] - pub struct TestServerCertVerifier<'a> { - scheme: SignatureScheme, - cert_der: CertificateDer<'a>, - } - - impl TestServerCertVerifier<'_> { - pub fn new(scheme: SignatureScheme, cert_path: PathBuf) -> Self { - let cert = fs::read(&cert_path).unwrap(); - let (_, pem) = parse_x509_pem(&cert).unwrap(); - let cert_der = CertificateDer::try_from(pem.contents).unwrap(); - Self { scheme, cert_der } - } - } - - impl ServerCertVerifier for TestServerCertVerifier<'_> { - fn verify_server_cert( - &self, - end_entity: &CertificateDer<'_>, - intermediates: &[CertificateDer<'_>], - server_name: &ServerName<'_>, - ocsp_response: &[u8], - now: UnixTime, - ) -> Result { - assert_eq!(&self.cert_der, end_entity); - Ok(ServerCertVerified::assertion()) - } - - fn verify_tls12_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - - fn verify_tls13_signature( - &self, - message: &[u8], - cert: &CertificateDer<'_>, - dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - - fn supported_verify_schemes(&self) -> Vec { - vec![self.scheme] - } - } - } } From 4efd5b65ebd531e737095b16132fe31b6f24910b Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 3 Apr 2024 01:29:17 +0500 Subject: [PATCH 036/129] Rm re-exports. Use tokio/macros for binaries feat only --- Cargo.toml | 4 +--- src/bin/loadtest.rs | 2 +- src/lib.rs | 4 ---- tests/tls.rs | 3 ++- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 031103fd..c70eea3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [".github", "docker", ".gitignore", "Makefile"] [features] default = [] tls = ["dep:tokio-native-tls", "dep:pin-project"] -binaries = ["clap"] +binaries = ["dep:clap", "tokio/macros"] ent = [] [dependencies] @@ -38,12 +38,10 @@ sha2 = "0.10.0" thiserror = "1.0.30" tokio = { version = "1.35.1", features = [ "io-util", - "macros", "net", "rt", "rt-multi-thread", "time", - ] } tokio-native-tls = { version = "0.3.1", optional = true } url = "2" diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index 13a92f74..095912ba 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -9,7 +9,7 @@ use std::time; const QUEUES: &[&str] = &["queue0", "queue1", "queue2", "queue3", "queue4"]; -#[faktory::main] +#[tokio::main] async fn main() { let matches = Command::new("My Super Program (Async)") .version("0.1") diff --git a/src/lib.rs b/src/lib.rs index 79538edb..8f4617e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,3 @@ mod tls; #[cfg(feature = "tls")] #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] pub use tls::TlsStream; - -// 3rd-party libs re-exports -pub use async_trait::async_trait; -pub use tokio::main; diff --git a/tests/tls.rs b/tests/tls.rs index e9dda2d7..d98294f4 100644 --- a/tests/tls.rs +++ b/tests/tls.rs @@ -56,7 +56,8 @@ mod fixtures { pub use handler::JobHandler; mod handler { - use faktory::{async_trait, Job, JobRunner}; + use async_trait::async_trait; + use faktory::{Job, JobRunner}; use std::{ io, sync::{mpsc::Sender, Arc, Mutex}, From d657f69e7a793538fab2745f75179139a500690f Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 3 Apr 2024 10:26:34 +0500 Subject: [PATCH 037/129] Restore pin of min version for openssl --- Cargo.lock | 1 + Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 9f5a807d..362a7651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -439,6 +439,7 @@ dependencies = [ "hostname", "num-bigint", "oid-registry", + "openssl", "pin-project", "rand", "rustls", diff --git a/Cargo.toml b/Cargo.toml index c70eea3b..e527a027 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ x509-parser = "0.15.1" # to make -Zminimal-versions work [target.'cfg(any())'.dependencies] +openssl = { version = "0.10.60", optional = true } rustls = "0.22.1" num-bigint = "0.4.2" oid-registry = "0.6.1" From 53969f78cbf4648ce023298cfbd36d3d3add25d0 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 3 Apr 2024 21:19:15 +0500 Subject: [PATCH 038/129] Use JoinSet in loadtest binary --- src/bin/loadtest.rs | 99 +++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index 095912ba..5dbc5054 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -6,6 +6,7 @@ use std::io; use std::process; use std::sync::{self, atomic}; use std::time; +use tokio::task; const QUEUES: &[&str] = &["queue0", "queue1", "queue2", "queue3", "queue4"]; @@ -49,60 +50,62 @@ async fn main() { let popped = sync::Arc::new(atomic::AtomicUsize::new(0)); let start = time::Instant::now(); - let threads: Vec>> = (0..threads) - .map(|_| { - let pushed = sync::Arc::clone(&pushed); - let popped = sync::Arc::clone(&popped); - tokio::spawn(async move { - // make producer and consumer - let mut p = Client::connect(None).await.unwrap(); - let mut c = WorkerBuilder::default(); - c.register("SomeJob", |_| { - Box::pin(async move { - let mut rng = rand::thread_rng(); - if rng.gen_bool(0.01) { - Err(io::Error::new(io::ErrorKind::Other, "worker closed")) - } else { - Ok(()) - } - }) - }); - let mut c = c.connect(None).await.unwrap(); - let mut rng = rand::rngs::OsRng; - let mut random_queues = Vec::from(QUEUES); - random_queues.shuffle(&mut rng); - for idx in 0..jobs { - if idx % 2 == 0 { - // push - let mut job = Job::new( - "SomeJob", - vec![serde_json::Value::from(1), "string".into(), 3.into()], - ); - job.priority = Some(rng.gen_range(1..10)); - job.queue = QUEUES.choose(&mut rng).unwrap().to_string(); - p.enqueue(job).await?; - if pushed.fetch_add(1, atomic::Ordering::SeqCst) >= jobs { - return Ok(idx); - } + let mut set = task::JoinSet::new(); + for _ in 0..threads { + let pushed = sync::Arc::clone(&pushed); + let popped = sync::Arc::clone(&popped); + set.spawn(async move { + // make producer and consumer + let mut p = Client::connect(None).await.unwrap(); + let mut c = WorkerBuilder::default(); + c.register("SomeJob", |_| { + Box::pin(async move { + let mut rng = rand::thread_rng(); + if rng.gen_bool(0.01) { + Err(io::Error::new(io::ErrorKind::Other, "worker closed")) } else { - // pop - c.run_one(0, &random_queues[..]).await?; - if popped.fetch_add(1, atomic::Ordering::SeqCst) >= jobs { - return Ok(idx); - } + Ok(()) + } + }) + }); + let mut c = c.connect(None).await.unwrap(); + + let mut rng = rand::rngs::OsRng; + let mut random_queues = Vec::from(QUEUES); + random_queues.shuffle(&mut rng); + for idx in 0..jobs { + if idx % 2 == 0 { + // push + let mut job = Job::new( + "SomeJob", + vec![serde_json::Value::from(1), "string".into(), 3.into()], + ); + job.priority = Some(rng.gen_range(1..10)); + job.queue = QUEUES.choose(&mut rng).unwrap().to_string(); + p.enqueue(job).await?; + if pushed.fetch_add(1, atomic::Ordering::SeqCst) >= jobs { + return Ok::(idx); + } + } else { + // pop + c.run_one(0, &random_queues[..]).await?; + if popped.fetch_add(1, atomic::Ordering::SeqCst) >= jobs { + return Ok(idx); } } - Ok(jobs) - }) - }) - .collect(); + } + Ok(jobs) + }); + } - let mut _ops_count = Vec::with_capacity(threads.len()); - for jh in threads { - _ops_count.push(jh.await.unwrap()) + let mut _ops_count = Vec::with_capacity(threads); + while let Some(res) = set.join_next().await { + _ops_count.push(res.unwrap()) } + let stop = start.elapsed(); + let stop_secs = stop.as_secs() * 1_000_000_000 + u64::from(stop.subsec_nanos()); let stop_secs = stop_secs as f64 / 1_000_000_000.0; println!( @@ -112,5 +115,5 @@ async fn main() { stop_secs, jobs as f64 / stop_secs, ); - // println!("{:?}", _ops_count); + println!("{:?}", _ops_count); } From ea5fa8bb925175a35da0b4608865acf01a6836b9 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 3 Apr 2024 21:55:00 +0500 Subject: [PATCH 039/129] Rm left-over from proto/mod --- src/proto/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/proto/mod.rs b/src/proto/mod.rs index ea23861b..3d7bf315 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -49,7 +49,6 @@ where S: AsyncRead + AsyncWrite + Reconnect + Send + Sync, { async fn reconnect(&mut self) -> io::Result { - // let addr = &self.get_ref().peer_addr().expect("socket address"); let stream = self.get_mut().reconnect().await?; Ok(Self::new(stream)) } From d3389caf6e8fa07d4784821e211790a7f381586c Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 3 Apr 2024 22:44:55 +0500 Subject: [PATCH 040/129] Keep 'CommitBatch' public in crate --- src/proto/batch/cmd.rs | 7 ++++--- src/proto/batch/mod.rs | 4 +++- src/proto/client/ent.rs | 4 ++-- src/proto/mod.rs | 3 +-- src/worker/runner.rs | 3 ++- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/proto/batch/cmd.rs b/src/proto/batch/cmd.rs index bd238736..cbf61c81 100644 --- a/src/proto/batch/cmd.rs +++ b/src/proto/batch/cmd.rs @@ -23,8 +23,9 @@ macro_rules! batch_cmd { #[async_trait::async_trait] impl FaktoryCommand for $structure { async fn issue(&self, w: &mut W) -> Result<(), Error> { - let c = format!("BATCH {} ", $cmd); - w.write_all(c.as_bytes()).await?; + w.write_all(b"BATCH ").await?; + w.write_all($cmd.as_bytes()).await?; + w.write_all(b" ").await?; w.write_all(self.0.as_bytes()).await?; Ok(w.write_all(b"\r\n").await?) } @@ -32,7 +33,7 @@ macro_rules! batch_cmd { }; } -pub struct CommitBatch(BatchId); +pub(crate) struct CommitBatch(BatchId); batch_cmd!(CommitBatch, "COMMIT"); pub struct GetBatchStatus(BatchId); diff --git a/src/proto/batch/mod.rs b/src/proto/batch/mod.rs index da5e656e..f274f19a 100644 --- a/src/proto/batch/mod.rs +++ b/src/proto/batch/mod.rs @@ -8,10 +8,12 @@ mod cmd; mod handle; mod status; -pub use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; +pub use cmd::{GetBatchStatus, OpenBatch}; pub use handle::BatchHandle; pub use status::{BatchStatus, CallbackState}; +pub(crate) use cmd::CommitBatch; + /// Batch of jobs. /// /// Faktory guarantees a callback (`success` and/or `failure`) will be triggered after the execution diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index e0876d09..b1a43378 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -1,6 +1,6 @@ +use super::super::batch::CommitBatch; use super::super::{ - single, BatchStatus, CommitBatch, GetBatchStatus, JobId, OpenBatch, Progress, ProgressUpdate, - Track, + single, BatchStatus, GetBatchStatus, JobId, OpenBatch, Progress, ProgressUpdate, Track, }; use super::{Client, ReadToken}; use crate::ent::{Batch, BatchHandle, BatchId}; diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 3d7bf315..b36a61af 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -24,8 +24,7 @@ pub use self::single::BatchId; mod batch; #[cfg(feature = "ent")] pub use batch::{ - Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, CommitBatch, GetBatchStatus, - OpenBatch, + Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, GetBatchStatus, OpenBatch, }; /// A stream that can be re-established after failing. diff --git a/src/worker/runner.rs b/src/worker/runner.rs index 98488d90..0bae806a 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -14,7 +14,8 @@ use std::future::Future; /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{async_trait, Job, JobRunner, WorkerBuilder}; +/// use async_trait::async_trait; +/// use faktory::{Job, JobRunner, WorkerBuilder}; /// use std::io; /// /// struct MyHandler { From b3a272d915e6a2eb26ef062dec92265052926884 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 3 Apr 2024 22:47:26 +0500 Subject: [PATCH 041/129] Keep 'GetBatchStatus' public in crate --- src/proto/batch/cmd.rs | 2 +- src/proto/batch/mod.rs | 4 ++-- src/proto/client/ent.rs | 6 ++---- src/proto/mod.rs | 4 +--- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/proto/batch/cmd.rs b/src/proto/batch/cmd.rs index cbf61c81..f6bd7dbf 100644 --- a/src/proto/batch/cmd.rs +++ b/src/proto/batch/cmd.rs @@ -36,7 +36,7 @@ macro_rules! batch_cmd { pub(crate) struct CommitBatch(BatchId); batch_cmd!(CommitBatch, "COMMIT"); -pub struct GetBatchStatus(BatchId); +pub(crate) struct GetBatchStatus(BatchId); batch_cmd!(GetBatchStatus, "STATUS"); pub struct OpenBatch(BatchId); diff --git a/src/proto/batch/mod.rs b/src/proto/batch/mod.rs index f274f19a..77cc1dd6 100644 --- a/src/proto/batch/mod.rs +++ b/src/proto/batch/mod.rs @@ -8,11 +8,11 @@ mod cmd; mod handle; mod status; -pub use cmd::{GetBatchStatus, OpenBatch}; +pub use cmd::OpenBatch; pub use handle::BatchHandle; pub use status::{BatchStatus, CallbackState}; -pub(crate) use cmd::CommitBatch; +pub(crate) use cmd::{CommitBatch, GetBatchStatus}; /// Batch of jobs. /// diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index b1a43378..c5d88be2 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -1,7 +1,5 @@ -use super::super::batch::CommitBatch; -use super::super::{ - single, BatchStatus, GetBatchStatus, JobId, OpenBatch, Progress, ProgressUpdate, Track, -}; +use super::super::batch::{CommitBatch, GetBatchStatus}; +use super::super::{single, BatchStatus, JobId, OpenBatch, Progress, ProgressUpdate, Track}; use super::{Client, ReadToken}; use crate::ent::{Batch, BatchHandle, BatchId}; use crate::error::Error; diff --git a/src/proto/mod.rs b/src/proto/mod.rs index b36a61af..89817af2 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -23,9 +23,7 @@ pub use self::single::BatchId; #[cfg(feature = "ent")] mod batch; #[cfg(feature = "ent")] -pub use batch::{ - Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, GetBatchStatus, OpenBatch, -}; +pub use batch::{Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, OpenBatch}; /// A stream that can be re-established after failing. #[async_trait::async_trait] From c70ff2c30aeed40fbc5e80757d6a9133339d9a84 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 3 Apr 2024 22:49:11 +0500 Subject: [PATCH 042/129] Keep 'OpenBatch' public in crate --- src/proto/batch/cmd.rs | 2 +- src/proto/batch/mod.rs | 3 +-- src/proto/client/ent.rs | 4 ++-- src/proto/mod.rs | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/proto/batch/cmd.rs b/src/proto/batch/cmd.rs index f6bd7dbf..27329a36 100644 --- a/src/proto/batch/cmd.rs +++ b/src/proto/batch/cmd.rs @@ -39,5 +39,5 @@ batch_cmd!(CommitBatch, "COMMIT"); pub(crate) struct GetBatchStatus(BatchId); batch_cmd!(GetBatchStatus, "STATUS"); -pub struct OpenBatch(BatchId); +pub(crate) struct OpenBatch(BatchId); batch_cmd!(OpenBatch, "OPEN"); diff --git a/src/proto/batch/mod.rs b/src/proto/batch/mod.rs index 77cc1dd6..52d7d617 100644 --- a/src/proto/batch/mod.rs +++ b/src/proto/batch/mod.rs @@ -8,11 +8,10 @@ mod cmd; mod handle; mod status; -pub use cmd::OpenBatch; pub use handle::BatchHandle; pub use status::{BatchStatus, CallbackState}; -pub(crate) use cmd::{CommitBatch, GetBatchStatus}; +pub(crate) use cmd::{CommitBatch, GetBatchStatus, OpenBatch}; /// Batch of jobs. /// diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index c5d88be2..c3c83daf 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -1,5 +1,5 @@ -use super::super::batch::{CommitBatch, GetBatchStatus}; -use super::super::{single, BatchStatus, JobId, OpenBatch, Progress, ProgressUpdate, Track}; +use super::super::batch::{CommitBatch, GetBatchStatus, OpenBatch}; +use super::super::{single, BatchStatus, JobId, Progress, ProgressUpdate, Track}; use super::{Client, ReadToken}; use crate::ent::{Batch, BatchHandle, BatchId}; use crate::error::Error; diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 89817af2..a6f968c7 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -23,7 +23,7 @@ pub use self::single::BatchId; #[cfg(feature = "ent")] mod batch; #[cfg(feature = "ent")] -pub use batch::{Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState, OpenBatch}; +pub use batch::{Batch, BatchBuilder, BatchHandle, BatchStatus, CallbackState}; /// A stream that can be re-established after failing. #[async_trait::async_trait] From a56137734cfd49ac8cbac10ee0d260c4aaaac8fc Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 4 Apr 2024 21:41:45 +0500 Subject: [PATCH 043/129] Clean up in Cleint::init --- src/proto/client/mod.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index fbdd1dc8..c468e344 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -228,6 +228,7 @@ where let hi = single::read_hi(&mut self.stream).await?; check_protocols_match(hi.version)?; + // fill in any missing options, and remember them for re-connect let mut hello = single::Hello::default(); // prepare password hash, if one expected by 'Faktory' @@ -239,12 +240,8 @@ where } } - // fill in any missing options, and remember them for re-connect - let mut hello = single::Hello::default(); - if self.opts.is_worker { // fill in any missing options, and remember them for re-connect - let hostname = self .opts .hostname @@ -263,14 +260,6 @@ where hello.labels.clone_from(&self.opts.labels); } - if hi.salt.is_some() { - if let Some(ref pwd) = self.opts.password { - hello.set_password(&hi, pwd); - } else { - return Err(error::Connect::AuthenticationNeeded.into()); - } - } - single::write_command_and_await_ok(&mut self.stream, &hello).await?; Ok(()) } From e2ccfcf321aeadf789f3009db756e84fcbeee94c Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 4 Apr 2024 21:56:04 +0500 Subject: [PATCH 044/129] Split private and public methods for Client into blocks --- src/proto/client/mod.rs | 55 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index c468e344..3f4a5d31 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -288,6 +288,36 @@ where .await } + pub(crate) async fn heartbeat(&mut self) -> Result { + single::write_command( + &mut self.stream, + &single::Heartbeat::new(self.opts.wid.as_ref().unwrap().clone()), + ) + .await?; + + match single::read_json::<_, serde_json::Value>(&mut self.stream).await? { + None => Ok(HeartbeatStatus::Ok), + Some(s) => match s + .as_object() + .and_then(|m| m.get("state")) + .and_then(|s| s.as_str()) + { + Some("terminate") => Ok(HeartbeatStatus::Terminate), + Some("quiet") => Ok(HeartbeatStatus::Quiet), + _ => Err(error::Protocol::BadType { + expected: "heartbeat response", + received: format!("{}", s), + } + .into()), + }, + } + } +} + +impl Client +where + S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, +{ /// Asynchronously enqueue the given job on the Faktory server. /// /// Returns `Ok` if the job was successfully queued by the Faktory server. @@ -360,31 +390,6 @@ where .read_ok() .await } - - pub(crate) async fn heartbeat(&mut self) -> Result { - single::write_command( - &mut self.stream, - &single::Heartbeat::new(self.opts.wid.as_ref().unwrap().clone()), - ) - .await?; - - match single::read_json::<_, serde_json::Value>(&mut self.stream).await? { - None => Ok(HeartbeatStatus::Ok), - Some(s) => match s - .as_object() - .and_then(|m| m.get("state")) - .and_then(|s| s.as_str()) - { - Some("terminate") => Ok(HeartbeatStatus::Terminate), - Some("quiet") => Ok(HeartbeatStatus::Quiet), - _ => Err(error::Protocol::BadType { - expected: "heartbeat response", - received: format!("{}", s), - } - .into()), - }, - } - } } pub struct ReadToken<'a, S>(pub(crate) &'a mut Client) From a1a701efdd48d5278e2a5530069dc3694a853e57 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 4 Apr 2024 22:56:49 +0500 Subject: [PATCH 045/129] Derive Debug for ClientOptions --- src/proto/client/options.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/proto/client/options.rs b/src/proto/client/options.rs index 71b432fb..98fa58ae 100644 --- a/src/proto/client/options.rs +++ b/src/proto/client/options.rs @@ -1,29 +1,23 @@ use crate::proto::WorkerId; -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct ClientOptions { - /// Hostname to advertise to server. - /// Defaults to machine hostname. + // Hostname to advertise to server. Defaults to machine hostname. pub(crate) hostname: Option, - /// PID to advertise to server. - /// Defaults to process ID. + // PID to advertise to server. Defaults to process ID. pub(crate) pid: Option, - /// Worker ID to advertise to server. - /// Defaults to a GUID. + // Worker ID to advertise to server Defaults to a GUID. pub(crate) wid: Option, - /// Labels to advertise to se/// A stream that can be re-established after failing.rver. - /// Defaults to ["rust"]. + // Labels to advertise to server. Defaults to ["rust"]. pub(crate) labels: Vec, - /// Password to authenticate with - /// Defaults to None. + // Password to authenticate with. Defaults to None. pub(crate) password: Option, - /// Whether this client is instatianted for - /// a worker (i.e. to consume jobs). + // Whether this client is instatianted for a worker (i.e. to consume jobs). pub(crate) is_worker: bool, } From 9e55c24210715077e2111b3d3933727719028665 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 4 Apr 2024 22:58:12 +0500 Subject: [PATCH 046/129] Update self_to_cmd to not use format --- src/proto/single/cmd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index 11453d35..dc24e343 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -13,8 +13,8 @@ macro_rules! self_to_cmd { #[async_trait::async_trait] impl FaktoryCommand for $struct { async fn issue(&self, w: &mut W) -> Result<(), Error> { - let c = format!("{} ", $cmd); - w.write_all(c.as_bytes()).await?; + w.write_all($cmd.as_bytes()).await?; + w.write_all(b" ").await?; let r = serde_json::to_vec(self).map_err(Error::Serialization)?; w.write_all(&r).await?; Ok(w.write_all(b"\r\n").await?) From 3aba8dac039ed5502f3c7e3e094920f4f65dd98b Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 4 Apr 2024 23:17:09 +0500 Subject: [PATCH 047/129] Add 'new' method for Ack cmd --- src/proto/single/cmd.rs | 11 ++++------- src/worker/mod.rs | 4 ++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index dc24e343..d8735c12 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -53,15 +53,12 @@ impl FaktoryCommand for Info { #[derive(Serialize)] pub struct Ack { - #[serde(rename = "jid")] - job_id: JobId, + jid: JobId, } -impl From<&JobId> for Ack { - fn from(job_id: &JobId) -> Self { - Ack { - job_id: job_id.to_owned(), - } +impl Ack { + pub fn new>(jid: J) -> Ack { + Ack { jid: jid.into() } } } diff --git a/src/worker/mod.rs b/src/worker/mod.rs index 0a4ce0c0..899ff556 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -179,7 +179,7 @@ impl Result<(), Error> { - self.c.issue(&Ack::from(&jid)).await?.read_ok().await + self.c.issue(&Ack::new(jid)).await?.read_ok().await } async fn report_on_all_workers(&mut self) -> Result<(), Error> { @@ -192,7 +192,7 @@ impl self.c.issue(&Ack::from(jid)).await, + Ok(ref jid) => self.c.issue(&Ack::new(jid.clone())).await, Err(ref fail) => self.c.issue(fail).await, }; From 8270b7ef111251d1108aad60ea5cdc6639ce4a82 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 4 Apr 2024 23:37:55 +0500 Subject: [PATCH 048/129] Add docs to 'generic' and 'generic_with_backtrace' --- src/proto/single/cmd.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index d8735c12..e425896e 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -93,7 +93,7 @@ pub struct Fail { } impl Fail { - pub fn new(job_id: JobId, kind: impl Into, message: impl Into) -> Self { + pub(crate) fn new(job_id: JobId, kind: impl Into, message: impl Into) -> Self { Fail { job_id, kind: kind.into(), @@ -102,16 +102,21 @@ impl Fail { } } - // "unknown" is the errtype used by the go library too - pub fn generic>(job_id: JobId, message: S) -> Self { + // Used for all kind of errors not related to domain logic, e.g. missing handler for this type of job (i.e. + // the worker consumed a job from a specified queue, but has no tool to process it). + // Note that "unknown" is the error type used by the Go library in such cases too. + pub(crate) fn generic>(job_id: JobId, message: S) -> Self { Fail::new(job_id, "unknown", message) } - pub fn set_backtrace(&mut self, lines: Vec) { + pub(crate) fn set_backtrace(&mut self, lines: Vec) { self.backtrace = lines; } - pub fn generic_with_backtrace(jid: JobId, e: E) -> Self + // For any application errors (all kind of errors that could happen in userland when handling the job) + // we want to send backtrace (split into lines) to the Faktory server so that whoever is interested in + // the job result could follow the trace (for debugging essentially). + pub(crate) fn generic_with_backtrace(jid: JobId, e: E) -> Self where E: StdError, { From b97071e3c6a1c4ab4fa01bd8b979b4ff8b83783f Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 4 Apr 2024 23:55:17 +0500 Subject: [PATCH 049/129] Only make commands pub(crate) --- src/proto/mod.rs | 7 ++++--- src/proto/single/cmd.rs | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/proto/mod.rs b/src/proto/mod.rs index a6f968c7..5768b085 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -9,9 +9,10 @@ pub(crate) use client::{ClientOptions, HeartbeatStatus, EXPECTED_PROTOCOL_VERSIO mod single; -pub use single::{ - Ack, Fail, Info, Job, JobBuilder, JobId, Push, PushBulk, QueueAction, QueueControl, WorkerId, -}; +pub use single::{Job, JobBuilder, JobId, WorkerId}; + +pub(crate) use single::{Ack, Fail, Info, Push, PushBulk, QueueAction, QueueControl}; + pub(crate) mod utils; #[cfg(feature = "ent")] diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index e425896e..cc1b7917 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -40,7 +40,7 @@ where // -------------------- INFO ---------------------- -pub struct Info; +pub(crate) struct Info; #[async_trait::async_trait] impl FaktoryCommand for Info { @@ -52,7 +52,7 @@ impl FaktoryCommand for Info { // -------------------- ACK ---------------------- #[derive(Serialize)] -pub struct Ack { +pub(crate) struct Ack { jid: JobId, } @@ -67,7 +67,7 @@ self_to_cmd!(Ack, "ACK"); // -------------------- BEAT ------------------ #[derive(Serialize)] -pub struct Heartbeat { +pub(crate) struct Heartbeat { wid: WorkerId, } @@ -82,7 +82,7 @@ self_to_cmd!(Heartbeat, "BEAT"); // -------------------- FAIL --------------------- #[derive(Serialize, Clone)] -pub struct Fail { +pub(crate) struct Fail { #[serde(rename = "jid")] job_id: JobId, #[serde(rename = "errtype")] @@ -136,7 +136,7 @@ self_to_cmd!(Fail, "FAIL"); // ---------------------- END -------------------- -pub struct End; +pub(crate) struct End; #[async_trait::async_trait] impl FaktoryCommand for End { @@ -147,7 +147,7 @@ impl FaktoryCommand for End { // --------------------- FETCH -------------------- -pub struct Fetch<'a, S> +pub(crate) struct Fetch<'a, S> where S: AsRef, { @@ -178,7 +178,7 @@ where // --------------------- HELLO -------------------- #[derive(Serialize)] -pub struct Hello { +pub(crate) struct Hello { #[serde(skip_serializing_if = "Option::is_none")] pub hostname: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -228,7 +228,7 @@ self_to_cmd!(Hello, "HELLO"); // --------------------- PUSH -------------------- -pub struct Push(Job); +pub(crate) struct Push(Job); use std::ops::Deref; impl Deref for Push { @@ -256,7 +256,7 @@ impl FaktoryCommand for Push { // ---------------------- PUSHB ------------------- -pub struct PushBulk(Vec); +pub(crate) struct PushBulk(Vec); impl From> for PushBulk { fn from(jobs: Vec) -> Self { @@ -276,12 +276,12 @@ impl FaktoryCommand for PushBulk { // ---------------------- QUEUE ------------------- -pub enum QueueAction { +pub(crate) enum QueueAction { Pause, Resume, } -pub struct QueueControl<'a, S> +pub(crate) struct QueueControl<'a, S> where S: AsRef, { From 6a32b4e14312440072a04db04a4267fc00f1b3e1 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Fri, 5 Apr 2024 09:30:52 +0500 Subject: [PATCH 050/129] Add disclaimer on batch and job progress: ent only --- src/proto/client/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 3f4a5d31..9a0b1efb 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -96,7 +96,8 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// /// `Client` is also useful for retrieving and updating information on a job's execution progress /// (see [`Progress`] and [`ProgressUpdate`]), as well for retrieving a batch's status -/// from the Faktory server (see [`BatchStatus`]). +/// from the Faktory server (see [`BatchStatus`]). But these constructs are only available under `ent` feature +/// and are only supported by Enterprise Faktory. /// /// Fetching a job's execution progress: /// From 84897e9c7c6ab2fae5df121126bfe13b712d80fc Mon Sep 17 00:00:00 2001 From: --show-origin Date: Fri, 5 Apr 2024 10:12:36 +0500 Subject: [PATCH 051/129] Make jid private again --- src/proto/single/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index 815c6a12..2c071a89 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -71,7 +71,7 @@ const JOB_DEFAULT_BACKTRACE: usize = 0; pub struct Job { /// The job's unique identifier. #[builder(default = "JobId::random()")] - pub jid: JobId, + pub(crate) jid: JobId, /// The queue this job belongs to. Usually `default`. #[builder(default = "JOB_DEFAULT_QUEUE.into()")] From fddd71f2e57be460d1f84e5a8c4c1a03df6b65c9 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Fri, 5 Apr 2024 11:40:04 +0500 Subject: [PATCH 052/129] Keep `args` private on `Job` --- src/proto/single/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index 2c071a89..c7f1a993 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -84,7 +84,7 @@ pub struct Job { /// The arguments provided for this job. #[builder(setter(custom), default = "Vec::new()")] - pub args: Vec, + pub(crate) args: Vec, /// When this job was created. // note that serializing works correctly here since the default chrono serialization @@ -163,9 +163,10 @@ impl JobBuilder { } /// Setter for the arguments provided for this job. - pub fn args(&mut self, args: Vec) -> &mut Self + pub fn args(&mut self, args: I) -> &mut Self where - A: Into, + I: IntoIterator, + V: Into, { self.args = Some(args.into_iter().map(|s| s.into()).collect()); self From 11c9a875421aff78bf27d69e33a3fea79ae509f9 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Fri, 5 Apr 2024 12:20:15 +0500 Subject: [PATCH 053/129] Rm rudimentary extern cate syntax from test binaries --- tests/consumer.rs | 4 ---- tests/producer.rs | 4 ---- 2 files changed, 8 deletions(-) diff --git a/tests/consumer.rs b/tests/consumer.rs index f6fade87..8aab27db 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -1,7 +1,3 @@ -extern crate faktory; -extern crate serde_json; -extern crate url; - mod mock; use faktory::*; diff --git a/tests/producer.rs b/tests/producer.rs index 3104c83f..63841f83 100644 --- a/tests/producer.rs +++ b/tests/producer.rs @@ -1,7 +1,3 @@ -extern crate faktory; -extern crate serde_json; -extern crate url; - mod mock; use faktory::*; From bfc76fcc65eb83fff8bf2918663300d1c9b92862 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Fri, 5 Apr 2024 12:22:29 +0500 Subject: [PATCH 054/129] Rm left-over from testing --- tests/producer.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/producer.rs b/tests/producer.rs index 63841f83..ee50d815 100644 --- a/tests/producer.rs +++ b/tests/producer.rs @@ -8,7 +8,6 @@ async fn hello() { let p = Client::connect_with(s.clone(), None).await.unwrap(); let written = s.pop_bytes_written(0); - eprintln!("{:?}", String::from_utf8(written.clone()).unwrap()); assert!(written.starts_with(b"HELLO {")); let written: serde_json::Value = serde_json::from_slice(&written[b"HELLO ".len()..]).unwrap(); let written = written.as_object().unwrap(); From 5ee03bd5cb515e25be3ce5901f92ecf320fff076 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Fri, 5 Apr 2024 12:31:00 +0500 Subject: [PATCH 055/129] Use mem::take in pop_bytes_written on MockStream --- tests/mock/inner.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/mock/inner.rs b/tests/mock/inner.rs index b7fd2272..85edc337 100644 --- a/tests/mock/inner.rs +++ b/tests/mock/inner.rs @@ -19,8 +19,7 @@ impl MockStream { pub fn pop_bytes_written(&mut self) -> Vec { let mut du = self.du.lock().unwrap(); - let mut wr = Vec::new(); - mem::swap(&mut wr, du.writer.get_mut()); + let wr = mem::take(du.writer.get_mut()); du.writer.set_position(0); wr } From 8da1e6cf5be3647dd6d0ea795529b5e747b60b00 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Fri, 5 Apr 2024 12:32:42 +0500 Subject: [PATCH 056/129] Rm left-over print stmts from testing from tests/mock --- tests/mock/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/mock/mod.rs b/tests/mock/mod.rs index 6a4707b4..17c45327 100644 --- a/tests/mock/mod.rs +++ b/tests/mock/mod.rs @@ -79,7 +79,6 @@ impl Stream { let streams = (0..streams) .map(|_| { let mut s = inner::MockStream::default(); - eprintln!("{:#?}", s); // need to say HELLO if let Some((iters, salt)) = salt { // include salt for pwdhash From 44a3b31a9645bc8e059cd174039ebf8e6147064e Mon Sep 17 00:00:00 2001 From: --show-origin Date: Fri, 5 Apr 2024 13:05:08 +0500 Subject: [PATCH 057/129] Restore hello_c test as hello_worker --- tests/real/community.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/real/community.rs b/tests/real/community.rs index dabf1ab4..4605cf8c 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -6,12 +6,23 @@ use serde_json::Value; use std::{io, sync}; #[tokio::test(flavor = "multi_thread")] -async fn hello_p() { +async fn hello_client() { skip_check!(); let p = Client::connect(None).await.unwrap(); drop(p); } +#[tokio::test(flavor = "multi_thread")] +async fn hello_worker() { + skip_check!(); + let mut c = WorkerBuilder::::default(); + c.hostname("tester".to_string()) + .labels(vec!["foo".to_string(), "bar".to_string()]); + c.register("never_called", |_| async move { unreachable!() }); + let c = c.connect(None).await.unwrap(); + drop(c); +} + #[tokio::test(flavor = "multi_thread")] async fn enqueue_job() { skip_check!(); From 8b6e784cf3d8f44bc7c9f4356e3fb6704fc4907f Mon Sep 17 00:00:00 2001 From: --show-origin Date: Fri, 5 Apr 2024 13:54:49 +0500 Subject: [PATCH 058/129] Clean up 'roundtrip' test --- tests/real/community.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/tests/real/community.rs b/tests/real/community.rs index 4605cf8c..c6701b28 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -30,38 +30,36 @@ async fn enqueue_job() { p.enqueue(JobBuilder::new("order").build()).await.unwrap(); } -async fn process_order(j: Job) -> Result<(), std::io::Error> { - println!("{:?}", j); - assert_eq!(j.kind(), "order"); - Ok(()) -} - #[tokio::test(flavor = "multi_thread")] async fn roundtrip() { skip_check!(); + + let local = "roundtrip"; let jid = String::from("x-job-id-0123456782"); + let mut c = WorkerBuilder::default(); - c.register("order", process_order); - c.register("image", |job| async move { - println!("{:?}", job); - assert_eq!(job.kind(), "image"); - Ok(()) + c.register("order", move |job| async move { + assert_eq!(job.kind(), "order"); + assert_eq!(job.queue, local); + assert_eq!(job.args(), &[Value::from("ISBN-13:9781718501850")]); + Ok::<(), io::Error>(()) }); + c.register("image", |_| async move { unreachable!() }); let mut c = c.connect(None).await.unwrap(); let mut p = Client::connect(None).await.unwrap(); p.enqueue( JobBuilder::new("order") .jid(&jid) .args(vec!["ISBN-13:9781718501850"]) - .queue("roundtrip") + .queue(local) .build(), ) .await .unwrap(); - let had_one = c.run_one(0, &["roundtrip"]).await.unwrap(); + let had_one = c.run_one(0, &[local]).await.unwrap(); assert!(had_one); - let drained = !c.run_one(0, &["roundtrip"]).await.unwrap(); + let drained = !c.run_one(0, &[local]).await.unwrap(); assert!(drained); } From b633b1925dab95f7ae9ab904ea5522de7162fb10 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 6 Apr 2024 22:17:15 +0500 Subject: [PATCH 059/129] Rm 'extern' declaration from tests/real --- tests/real/community.rs | 2 -- tests/real/enterprise.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/real/community.rs b/tests/real/community.rs index c6701b28..d8b36885 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -1,5 +1,3 @@ -extern crate faktory; - use crate::skip_check; use faktory::{Client, Job, JobBuilder, WorkerBuilder}; use serde_json::Value; diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index 26381d73..74bb5ff0 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -1,5 +1,3 @@ -extern crate faktory; - use crate::skip_if_not_enterprise; use crate::utils::learn_faktory_url; use chrono::Utc; From dd3a9892ef6e3443fa7fc7b359220219b4dc01a7 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 7 Apr 2024 18:04:06 +0500 Subject: [PATCH 060/129] Make min-versions pass again --- Cargo.lock | 154 +++++++++++++++-------------------------------------- Cargo.toml | 2 +- 2 files changed, 43 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 362a7651..34dcb7a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,31 +138,31 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] name = "async-trait" -version = "0.1.78" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -202,15 +202,15 @@ checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.90" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "1fd97381a8cc6493395a5afc4c691c1084b3768db713b73aa215217aa245d153" [[package]] name = "cfg-if" @@ -220,9 +220,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", @@ -235,9 +235,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.3" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", ] @@ -251,7 +251,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.0", + "strsim 0.11.1", ] [[package]] @@ -414,7 +414,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] @@ -437,12 +437,12 @@ dependencies = [ "derive_builder", "fnv", "hostname", + "native-tls", "num-bigint", "oid-registry", "openssl", "pin-project", "rand", - "rustls", "serde", "serde_derive", "serde_json", @@ -509,9 +509,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6" dependencies = [ "cfg-if", "libc", @@ -582,9 +582,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" @@ -627,9 +627,9 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "minimal-lexical" @@ -777,7 +777,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] @@ -821,14 +821,14 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pkg-config" @@ -896,21 +896,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -939,37 +924,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" -dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" - -[[package]] -name = "rustls-webpki" -version = "0.102.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "ryu" version = "1.0.17" @@ -1025,14 +979,14 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" dependencies = [ "itoa", "ryu", @@ -1060,12 +1014,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "strsim" version = "0.10.0" @@ -1074,15 +1022,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strsim" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" @@ -1097,9 +1039,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.53" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -1147,7 +1089,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] @@ -1198,9 +1140,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.36.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -1221,7 +1163,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", ] [[package]] @@ -1291,12 +1233,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - [[package]] name = "url" version = "2.5.0" @@ -1353,7 +1289,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", "wasm-bindgen-shared", ] @@ -1375,7 +1311,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.53", + "syn 2.0.58", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1565,9 +1501,3 @@ dependencies = [ "thiserror", "time", ] - -[[package]] -name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index e527a027..938f6729 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,7 @@ x509-parser = "0.15.1" # to make -Zminimal-versions work [target.'cfg(any())'.dependencies] openssl = { version = "0.10.60", optional = true } -rustls = "0.22.1" +native-tls = { version = "0.2.4", optional = true } num-bigint = "0.4.2" oid-registry = "0.6.1" chrono = "0.4.32" From 802c1db940e1d6693b694c3096a5e14fa3fbdc1c Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 9 Apr 2024 22:03:51 +0500 Subject: [PATCH 061/129] Support remove queues, as well as pause all / resume all --- .gitignore | 1 + src/proto/client/mod.rs | 68 ++++++++++++++++++++++++++++++----- src/proto/single/cmd.rs | 2 ++ tests/real/community.rs | 79 +++++++++++++++++++++++++++++++++++------ 4 files changed, 131 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 0f735146..01cc43f3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **/*.rs.bk perf.* .vscode +mutants.* \ No newline at end of file diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 9a0b1efb..7cc7fac2 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -313,6 +313,20 @@ where }, } } + + pub(crate) async fn perform_queue_action( + &mut self, + queues: &[Q], + action: QueueAction, + ) -> Result<(), Error> + where + Q: AsRef + Sync, + { + self.issue(&QueueControl::new(action, queues)) + .await? + .read_ok() + .await + } } impl Client @@ -371,25 +385,63 @@ where } /// Pause the given queues. + /// + /// Passing a wildcard `&["*"]` as the value of the `queues` parameter + /// will pause all the queues. To be more explicit, you may want to call [`Client::queue_pause_all`] + /// shortcut method to pause all the queues. pub async fn queue_pause(&mut self, queues: &[Q]) -> Result<(), Error> where Q: AsRef + Sync, { - self.issue(&QueueControl::new(QueueAction::Pause, queues)) - .await? - .read_ok() - .await + self.perform_queue_action(queues, QueueAction::Pause).await + } + + /// Pause all queues. + pub async fn queue_pause_all(&mut self) -> Result<(), Error> + where + Q: AsRef + Sync, + { + self.perform_queue_action(&["*"], QueueAction::Pause).await } /// Resume the given queues. + /// + /// Passing a wildcard `&["*"]` as the value of the `queues` parameter + /// will resume all the queues. To be more explicit, you may want to call [`Client::queue_resume_all`] + /// shortcut method to resume all the queues. pub async fn queue_resume(&mut self, queues: &[Q]) -> Result<(), Error> where Q: AsRef + Sync, { - self.issue(&QueueControl::new(QueueAction::Resume, queues)) - .await? - .read_ok() - .await + self.perform_queue_action(queues, QueueAction::Resume).await + } + + /// Resume all queues. + pub async fn queue_resume_all(&mut self) -> Result<(), Error> + where + Q: AsRef + Sync, + { + self.perform_queue_action(&["*"], QueueAction::Resume).await + } + + /// Remove the given queues. + /// + /// Beware, passing a wildcard `&["*"]` as the value of the `queues` parameter + /// will **remove** all the queues. To be more explicit, you may want to call [`Client::queue_remove_all`] + /// shortcut method to remove all the queues. + pub async fn queue_remove(&mut self, queues: &[Q]) -> Result<(), Error> + where + Q: AsRef + Sync, + { + self.perform_queue_action(queues, QueueAction::Remove).await + } + + /// Remove all queues. + pub async fn queue_remove_all(&mut self) -> Result<(), Error> + where + Q: AsRef + Sync, + { + self.perform_queue_action(&["*"], QueueAction::Remove).await } } diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index cc1b7917..7f694e6e 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -279,6 +279,7 @@ impl FaktoryCommand for PushBulk { pub(crate) enum QueueAction { Pause, Resume, + Remove, } pub(crate) struct QueueControl<'a, S> @@ -298,6 +299,7 @@ where let command = match self.action { QueueAction::Pause => b"QUEUE PAUSE".as_ref(), QueueAction::Resume => b"QUEUE RESUME".as_ref(), + QueueAction::Remove => b"QUEUE REMOVE".as_ref(), }; w.write_all(command).await?; write_queues(w, self.queues).await?; diff --git a/tests/real/community.rs b/tests/real/community.rs index d8b36885..a3929e03 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -141,36 +141,93 @@ async fn fail() { #[tokio::test(flavor = "multi_thread")] async fn queue() { skip_check!(); - let local = "pause"; + + let local_1 = "queue_control_pause_and_resume_1"; + let local_2 = "queue_control_pause_and_resume_2"; let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); - let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local.into()); - c.register(local, move |_job| { + c.hostname("tester".to_string()).wid(local_1.into()); + { + let tx = sync::Arc::clone(&tx); + c.register(local_1, move |_job| { + let tx = sync::Arc::clone(&tx); + Box::pin(async move { tx.lock().unwrap().send(true) }) + }); + } + c.register(local_2, move |_job| { let tx = sync::Arc::clone(&tx); Box::pin(async move { tx.lock().unwrap().send(true) }) }); - let mut c = c.connect(None).await.unwrap(); - let mut p = Client::connect(None).await.unwrap(); - p.enqueue(Job::new(local, vec![Value::from(1)]).on_queue(local)) + let mut worker = c.connect(None).await.unwrap(); + + let mut client = Client::connect(None).await.unwrap(); + + // enqueue three jobs + client + .enqueue_many([ + Job::new(local_1, vec![Value::from(1)]).on_queue(local_1), + Job::new(local_1, vec![Value::from(1)]).on_queue(local_1), + Job::new(local_1, vec![Value::from(1)]).on_queue(local_1), + ]) .await .unwrap(); - p.queue_pause(&[local]).await.unwrap(); - let had_job = c.run_one(0, &[local]).await.unwrap(); + // pause the queue + client.queue_pause(&[local_1]).await.unwrap(); + + // try to consume from that queue + let had_job = worker.run_one(0, &[local_1]).await.unwrap(); assert!(!had_job); let worker_executed = rx.try_recv().is_ok(); assert!(!worker_executed); - p.queue_resume(&[local]).await.unwrap(); + // resume that queue and ... + client.queue_resume(&[local_1]).await.unwrap(); - let had_job = c.run_one(0, &[local]).await.unwrap(); + // ... be able to consume from it + let had_job = worker.run_one(0, &[local_1]).await.unwrap(); assert!(had_job); let worker_executed = rx.try_recv().is_ok(); assert!(worker_executed); + + // push two jobs on the other queue (reminder: we got two jobs + // remaining on the first queue): + client + .enqueue_many([ + Job::new(local_2, vec![Value::from(1)]).on_queue(local_2), + Job::new(local_2, vec![Value::from(1)]).on_queue(local_2), + ]) + .await + .unwrap(); + + // pause both queues the queues + client.queue_pause(&[local_1, local_2]).await.unwrap(); + + // try to consume from them + assert!(!worker.run_one(0, &[local_1]).await.unwrap()); + assert!(!worker.run_one(0, &[local_2]).await.unwrap()); + assert!(!rx.try_recv().is_ok()); + + // now, resume the queues and ... + client.queue_resume(&[local_1, local_2]).await.unwrap(); + + // ... be able to consume from both of them + assert!(worker.run_one(0, &[local_1]).await.unwrap()); + assert!(rx.try_recv().is_ok()); + assert!(worker.run_one(0, &[local_2]).await.unwrap()); + assert!(rx.try_recv().is_ok()); + + // let's now remove the queues + client.queue_remove(&[local_1, local_2]).await.unwrap(); + + // though there _was_ a job in each queue, consuming from + // the removed queues will not yield anything + assert!(!worker.run_one(0, &[local_1]).await.unwrap()); + assert!(!worker.run_one(0, &[local_2]).await.unwrap()); + assert!(!rx.try_recv().is_ok()); } #[tokio::test(flavor = "multi_thread")] From 4b2b81944c88bfb57356cffc56a9dace24635ade Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 10 Apr 2024 00:04:03 +0500 Subject: [PATCH 062/129] Add test for wirldcard queue control actions --- src/proto/client/mod.rs | 15 ++------- tests/real/community.rs | 68 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 7cc7fac2..893f5bd7 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -397,10 +397,7 @@ where } /// Pause all queues. - pub async fn queue_pause_all(&mut self) -> Result<(), Error> - where - Q: AsRef + Sync, - { + pub async fn queue_pause_all(&mut self) -> Result<(), Error> { self.perform_queue_action(&["*"], QueueAction::Pause).await } @@ -417,10 +414,7 @@ where } /// Resume all queues. - pub async fn queue_resume_all(&mut self) -> Result<(), Error> - where - Q: AsRef + Sync, - { + pub async fn queue_resume_all(&mut self) -> Result<(), Error> { self.perform_queue_action(&["*"], QueueAction::Resume).await } @@ -437,10 +431,7 @@ where } /// Remove all queues. - pub async fn queue_remove_all(&mut self) -> Result<(), Error> - where - Q: AsRef + Sync, - { + pub async fn queue_remove_all(&mut self) -> Result<(), Error> { self.perform_queue_action(&["*"], QueueAction::Remove).await } } diff --git a/tests/real/community.rs b/tests/real/community.rs index a3929e03..882a4406 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -139,7 +139,7 @@ async fn fail() { } #[tokio::test(flavor = "multi_thread")] -async fn queue() { +async fn queue_control_actions() { skip_check!(); let local_1 = "queue_control_pause_and_resume_1"; @@ -230,6 +230,72 @@ async fn queue() { assert!(!rx.try_recv().is_ok()); } +#[tokio::test(flavor = "multi_thread")] +#[ignore = "This requires a dedicated test run since the commands affect all queues on the Faktory server"] +async fn queue_control_actions_wildcard() { + skip_check!(); + + let local_1 = "queue_control_wildcard_1"; + let local_2 = "queue_control_wildcard_2"; + + let (tx, rx) = sync::mpsc::channel(); + let tx = sync::Arc::new(sync::Mutex::new(tx)); + let mut c = WorkerBuilder::default(); + c.hostname("tester".to_string()).wid(local_1.into()); + { + let tx = sync::Arc::clone(&tx); + c.register(local_1, move |_job| { + let tx = sync::Arc::clone(&tx); + Box::pin(async move { tx.lock().unwrap().send(true) }) + }); + } + c.register(local_2, move |_job| { + let tx = sync::Arc::clone(&tx); + Box::pin(async move { tx.lock().unwrap().send(true) }) + }); + + let mut worker = c.connect(None).await.unwrap(); + + let mut client = Client::connect(None).await.unwrap(); + + // enqueue two jobs on each queue + client + .enqueue_many([ + Job::new(local_1, vec![Value::from(1)]).on_queue(local_1), + Job::new(local_1, vec![Value::from(1)]).on_queue(local_1), + Job::new(local_2, vec![Value::from(1)]).on_queue(local_2), + Job::new(local_2, vec![Value::from(1)]).on_queue(local_2), + ]) + .await + .unwrap(); + + // pause all queues the queues + client.queue_pause_all().await.unwrap(); + + // try to consume from queues + assert!(!worker.run_one(0, &[local_1]).await.unwrap()); + assert!(!worker.run_one(0, &[local_2]).await.unwrap()); + assert!(!rx.try_recv().is_ok()); + + // now, resume all the queues and ... + client.queue_resume_all().await.unwrap(); + + // ... be able to consume from both of them + assert!(worker.run_one(0, &[local_1]).await.unwrap()); + assert!(rx.try_recv().is_ok()); + assert!(worker.run_one(0, &[local_2]).await.unwrap()); + assert!(rx.try_recv().is_ok()); + + // let's now remove all the queues + client.queue_remove_all().await.unwrap(); + + // though there _was_ a job in each queue, consuming from + // the removed queues will not yield anything + assert!(!worker.run_one(0, &[local_1]).await.unwrap()); + assert!(!worker.run_one(0, &[local_2]).await.unwrap()); + assert!(!rx.try_recv().is_ok()); +} + #[tokio::test(flavor = "multi_thread")] async fn test_jobs_pushed_in_bulk() { skip_check!(); From 8ba888d46f0b3b56d2b9b84ce5e5652aa9dad8a6 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 10 Apr 2024 00:15:54 +0500 Subject: [PATCH 063/129] Run wildcard queue control actions test as dedicated step on CI --- .github/workflows/test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b42d5e73..f3914872 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,6 +42,12 @@ jobs: run: cargo test --locked --all-features --all-targets env: # set this explicitly so integration tests will run FAKTORY_URL: tcp://127.0.0.1:7419 + # commands executed during the following test affect all the queues on the Faktory server, + # so we perform this test in a dedicated - isolated - step, re-using the the Faktory container + - name: cargo test --locked (queue control actions) + run: cargo test --locked --all-features --all-targets queue_control_actions_wildcard -- --include-ignored + env: # set this explicitly so integration tests will run + FAKTORY_URL: tcp://127.0.0.1:7419 # https://github.com/rust-lang/cargo/issues/6669 - name: cargo test --doc run: cargo test --locked --all-features --doc From 7bbf8268ed6c6cd2d828e7020ffe61d66d27950b Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 10 Apr 2024 00:21:21 +0500 Subject: [PATCH 064/129] Add test run commmand comment --- tests/real/community.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/real/community.rs b/tests/real/community.rs index 882a4406..10896653 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -230,6 +230,8 @@ async fn queue_control_actions() { assert!(!rx.try_recv().is_ok()); } +// Run the following test with: +// FAKTORY_URL=tcp://127.0.0.1:7419 cargo test --locked --all-features --all-targets queue_control_actions_wildcard -- --include-ignored #[tokio::test(flavor = "multi_thread")] #[ignore = "This requires a dedicated test run since the commands affect all queues on the Faktory server"] async fn queue_control_actions_wildcard() { From c49f972969e598fe9c2fa00a886cf68323bb4132 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 10 Apr 2024 10:30:57 +0500 Subject: [PATCH 065/129] Double-check queues are removed from Faktory --- tests/real/community.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/real/community.rs b/tests/real/community.rs index 10896653..b473118f 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -220,6 +220,12 @@ async fn queue_control_actions() { assert!(worker.run_one(0, &[local_2]).await.unwrap()); assert!(rx.try_recv().is_ok()); + // let's inspect the sever state + let server_state = client.info().await.unwrap(); + let queues = server_state.get("faktory").unwrap().get("queues").unwrap(); + assert_eq!(queues.get(local_1).unwrap(), 1); // 1 job remaining + assert_eq!(queues.get(local_2).unwrap(), 1); // also 1 job remaining + // let's now remove the queues client.queue_remove(&[local_1, local_2]).await.unwrap(); @@ -228,12 +234,19 @@ async fn queue_control_actions() { assert!(!worker.run_one(0, &[local_1]).await.unwrap()); assert!(!worker.run_one(0, &[local_2]).await.unwrap()); assert!(!rx.try_recv().is_ok()); + + // let's inspect the sever state again + let server_state = client.info().await.unwrap(); + let queues = server_state.get("faktory").unwrap().get("queues").unwrap(); + // our queue are not even mentioned in the server report: + assert!(queues.get(local_1).is_none()); + assert!(queues.get(local_2).is_none()); } // Run the following test with: // FAKTORY_URL=tcp://127.0.0.1:7419 cargo test --locked --all-features --all-targets queue_control_actions_wildcard -- --include-ignored #[tokio::test(flavor = "multi_thread")] -#[ignore = "This requires a dedicated test run since the commands affect all queues on the Faktory server"] +#[ignore = "this test requires a dedicated test run since the commands affect all queues on the Faktory server"] async fn queue_control_actions_wildcard() { skip_check!(); @@ -288,6 +301,12 @@ async fn queue_control_actions_wildcard() { assert!(worker.run_one(0, &[local_2]).await.unwrap()); assert!(rx.try_recv().is_ok()); + // let's inspect the sever state + let server_state = client.info().await.unwrap(); + let queues = server_state.get("faktory").unwrap().get("queues").unwrap(); + assert_eq!(queues.get(local_1).unwrap(), 1); // 1 job remaining + assert_eq!(queues.get(local_2).unwrap(), 1); // also 1 job remaining + // let's now remove all the queues client.queue_remove_all().await.unwrap(); @@ -296,6 +315,13 @@ async fn queue_control_actions_wildcard() { assert!(!worker.run_one(0, &[local_1]).await.unwrap()); assert!(!worker.run_one(0, &[local_2]).await.unwrap()); assert!(!rx.try_recv().is_ok()); + + // let's inspect the sever state again + let server_state = client.info().await.unwrap(); + let queues = server_state.get("faktory").unwrap().get("queues").unwrap(); + // our queue are not even mentioned in the server report: + assert!(queues.get(local_1).is_none()); + assert!(queues.get(local_2).is_none()); } #[tokio::test(flavor = "multi_thread")] From 8cb472e33bf66af76142f08b52474cfd016405d6 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 18 Apr 2024 20:03:42 +0500 Subject: [PATCH 066/129] Make `Client::info` return `ServerState` struct --- Makefile | 2 +- src/lib.rs | 5 ++- src/proto/client/mod.rs | 4 +- src/proto/mod.rs | 4 +- src/proto/single/resp.rs | 85 ++++++++++++++++++++++++++++++++++++++++ tests/real/community.rs | 73 ++++++++++++++++++++++++++++++---- 6 files changed, 160 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 35cac22a..6ff9a9bd 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ faktory/tls: .PHONY: faktory/tls/kill faktory/tls/kill: - docker compose -f docker/compose.yml down + docker compose -f docker/compose.yml down -v .PHONY: test test: diff --git a/src/lib.rs b/src/lib.rs index 8f4617e0..f282e408 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,10 @@ mod proto; mod worker; pub use crate::error::Error; -pub use crate::proto::{Client, Job, JobBuilder, JobId, Reconnect, WorkerId}; +pub use crate::proto::{ + Client, FaktoryServerProcessStats, FaktoryServiceStats, Job, JobBuilder, JobId, Reconnect, + ServerState, WorkerId, +}; pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; #[cfg(feature = "ent")] diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 893f5bd7..ce6fa4e1 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -373,10 +373,10 @@ where Ok((jobs_count - errors.len(), Some(errors))) } - /// Retrieve information about the running server. + /// Retrieve [information](crate::ServerState) about the running server. /// /// The returned value is the result of running the `INFO` command on the server. - pub async fn info(&mut self) -> Result { + pub async fn info(&mut self) -> Result { self.issue(&Info) .await? .read_json() diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 5768b085..a92788d6 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -9,7 +9,9 @@ pub(crate) use client::{ClientOptions, HeartbeatStatus, EXPECTED_PROTOCOL_VERSIO mod single; -pub use single::{Job, JobBuilder, JobId, WorkerId}; +pub use single::{ + FaktoryServerProcessStats, FaktoryServiceStats, Job, JobBuilder, JobId, ServerState, WorkerId, +}; pub(crate) use single::{Ack, Fail, Info, Push, PushBulk, QueueAction, QueueControl}; diff --git a/src/proto/single/resp.rs b/src/proto/single/resp.rs index 18e5a9b1..b24a9d72 100644 --- a/src/proto/single/resp.rs +++ b/src/proto/single/resp.rs @@ -1,7 +1,10 @@ +use std::collections::HashMap; + #[cfg(feature = "ent")] use crate::ent::BatchId; use crate::error::{self, Error}; +use chrono::{DateTime, Utc}; use tokio::io::{AsyncBufReadExt, AsyncReadExt}; pub fn bad(expected: &'static str, got: &RawResponse) -> error::Protocol { @@ -117,6 +120,88 @@ pub async fn read_ok(r: R) -> Result<(), Error> { Err(bad("server ok", &rr).into()) } +// ---------------------------------------------- + +/// Faktory service stats. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct FaktoryServiceStats { + /// Total number of job failures. + pub total_failures: u64, + + /// Total number of processed jobs. + pub total_processed: u64, + + /// Total number of enqueued jobs. + pub total_enqueued: u64, + + /// Total number of queues. + pub total_queues: u64, + + /// Queues stats. + /// + /// A mapping between a queue name and its size (number of jobs on the queue). + /// The keys of this map effectively make up a list of queues that are currently + /// registered in the Faktory service. + pub queues: HashMap, + + /// Faktory's task runner stats. + /// + /// Note that this is exposed as a "generic" `serde_json::Value` since this info + /// belongs to deep implementation details of the Faktory service. + pub tasks: serde_json::Value, +} + +/// Faktory's server process stats. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct FaktoryServerProcessStats { + /// Faktory's description (e.g. "Faktory"). + pub description: String, + + /// Faktory's version as semver (e.g. "1.8.0"). + pub faktory_version: String, + + /// Faktory server process uptime in seconds. + pub uptime: u64, + + /// Number of clients connected to the server. + pub connections: u64, + + /// Number of executed commands. + pub command_count: u64, + + /// Faktory server process memory usage. + pub used_memory_mb: u64, +} + +/// Current server state. +/// +/// Contains such details as how many queues there are on the server, statistics on the jobs, +/// as well as some specific info on server process memory usage, uptime, etc. +/// +/// Here is an example of the simplest way to fetch info on the server state. +/// ```no_run +/// # tokio_test::block_on(async { +/// use faktory::Client; +/// +/// let mut client = Client::connect(None).await.unwrap(); +/// let _server_state = client.info().await.unwrap(); +/// # }); +/// ``` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ServerState { + /// Server time. + pub now: DateTime, + + /// Server time as a string formatted as "%H:%M:%S UTC" (e.g. "19:47:39 UTC"). + pub server_utc_time: String, + + /// Faktory service stats. + pub faktory: FaktoryServiceStats, + + /// Faktory's server process stats. + pub server: FaktoryServerProcessStats, +} + // ---------------------------------------------- // // below is the implementation of the Redis RESP protocol diff --git a/tests/real/community.rs b/tests/real/community.rs index b473118f..9c1454bc 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -61,6 +61,63 @@ async fn roundtrip() { assert!(drained); } +#[tokio::test(flavor = "multi_thread")] +async fn server_state() { + skip_check!(); + + let local = "server_state"; + + // prepare a worker + let mut w = WorkerBuilder::default(); + w.register(local, move |_| async move { Ok::<(), io::Error>(()) }); + let mut w = w.connect(None).await.unwrap(); + + // prepare a producing client + let mut client = Client::connect(None).await.unwrap(); + + // examine server state before pushing anything + let server_state = client.info().await.unwrap(); + assert_eq!(*server_state.faktory.queues.get(local).unwrap(), 0); + + // the following two assertions are not super-helpful but + // there is not much info we can make meaningful assetions on anyhow + // (like memusage, server description string, version, etc.) + assert!(server_state.server.connections >= 2); // at least two clients from the current test + assert!(server_state.server.uptime > 0); // if IPC is happenning, this should hold :) + + // push 1 job + client + .enqueue( + JobBuilder::new(local) + .args(vec!["abc"]) + .queue(local) + .build(), + ) + .await + .unwrap(); + + // we only pushed 1 job on this queue + let server_state = client.info().await.unwrap(); + assert_eq!(*server_state.faktory.queues.get(local).unwrap(), 1); + assert!(server_state.faktory.total_enqueued >= 1); // at least 1 job from this test + assert!(server_state.faktory.total_queues >= 1); // at least 1 qeueu from this test + + // let's know consume that job ... + assert!(w.run_one(0, &[local]).await.unwrap()); + + // ... and verify the queue has got 0 pending jobs + // + // NB! If this is not passing locally, make sure to launch a fresh Faktory container, + // because if you have not pruned its volume _AND_ there somehow used to be a pending job + // on this local queue, you can imagine the test will fail. But generally, by consuming + // the jobs from the local queue, we are getting a clean up for free and there is normally + // no need to purge docker volumes to perform the next test run. + // Also note that on CI we are always starting a-fresh. + let server_state = client.info().await.unwrap(); + assert_eq!(*server_state.faktory.queues.get(local).unwrap(), 0); + assert!(server_state.faktory.total_processed >= 1); // at least 1 job from this test +} + #[tokio::test(flavor = "multi_thread")] async fn multi() { skip_check!(); @@ -222,9 +279,9 @@ async fn queue_control_actions() { // let's inspect the sever state let server_state = client.info().await.unwrap(); - let queues = server_state.get("faktory").unwrap().get("queues").unwrap(); - assert_eq!(queues.get(local_1).unwrap(), 1); // 1 job remaining - assert_eq!(queues.get(local_2).unwrap(), 1); // also 1 job remaining + let queues = &server_state.faktory.queues; + assert_eq!(*queues.get(local_1).unwrap(), 1); // 1 job remaining + assert_eq!(*queues.get(local_2).unwrap(), 1); // also 1 job remaining // let's now remove the queues client.queue_remove(&[local_1, local_2]).await.unwrap(); @@ -237,7 +294,7 @@ async fn queue_control_actions() { // let's inspect the sever state again let server_state = client.info().await.unwrap(); - let queues = server_state.get("faktory").unwrap().get("queues").unwrap(); + let queues = &server_state.faktory.queues; // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); assert!(queues.get(local_2).is_none()); @@ -303,9 +360,9 @@ async fn queue_control_actions_wildcard() { // let's inspect the sever state let server_state = client.info().await.unwrap(); - let queues = server_state.get("faktory").unwrap().get("queues").unwrap(); - assert_eq!(queues.get(local_1).unwrap(), 1); // 1 job remaining - assert_eq!(queues.get(local_2).unwrap(), 1); // also 1 job remaining + let queues = &server_state.faktory.queues; + assert_eq!(*queues.get(local_1).unwrap(), 1); // 1 job remaining + assert_eq!(*queues.get(local_2).unwrap(), 1); // also 1 job remaining // let's now remove all the queues client.queue_remove_all().await.unwrap(); @@ -318,7 +375,7 @@ async fn queue_control_actions_wildcard() { // let's inspect the sever state again let server_state = client.info().await.unwrap(); - let queues = server_state.get("faktory").unwrap().get("queues").unwrap(); + let queues = &server_state.faktory.queues; // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); assert!(queues.get(local_2).is_none()); From c1251b3a7b75b85daa937310fa0780e9500773a3 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 18 Apr 2024 21:10:13 +0500 Subject: [PATCH 067/129] Adjust test: Faktory does not know about queue just yet --- tests/real/community.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/real/community.rs b/tests/real/community.rs index 9c1454bc..aeb6f80c 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -77,8 +77,7 @@ async fn server_state() { // examine server state before pushing anything let server_state = client.info().await.unwrap(); - assert_eq!(*server_state.faktory.queues.get(local).unwrap(), 0); - + assert!(server_state.faktory.queues.get(local).is_none()); // the following two assertions are not super-helpful but // there is not much info we can make meaningful assetions on anyhow // (like memusage, server description string, version, etc.) From 9365b9d929fcbb5a7ccd4490e09215aa014a4a52 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Thu, 18 Apr 2024 21:41:00 +0500 Subject: [PATCH 068/129] Rm queue as test clean up step --- tests/real/community.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/real/community.rs b/tests/real/community.rs index aeb6f80c..7cb128e1 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -107,14 +107,25 @@ async fn server_state() { // ... and verify the queue has got 0 pending jobs // // NB! If this is not passing locally, make sure to launch a fresh Faktory container, - // because if you have not pruned its volume _AND_ there somehow used to be a pending job - // on this local queue, you can imagine the test will fail. But generally, by consuming - // the jobs from the local queue, we are getting a clean up for free and there is normally - // no need to purge docker volumes to perform the next test run. - // Also note that on CI we are always starting a-fresh. + // because if you have not pruned its volume the Faktory will still keep the queue name + // as registered. + // But generally, we are performing a clean-up by consuming the jobs from the local queue/ + // and then deleting the queue programmatically, so there is normally no need to prune docker + // volumes to perform the next test run. Also note that on CI we are always starting a-fresh. let server_state = client.info().await.unwrap(); assert_eq!(*server_state.faktory.queues.get(local).unwrap(), 0); assert!(server_state.faktory.total_processed >= 1); // at least 1 job from this test + + client.queue_remove(&[local]).await.unwrap(); + + assert!(client + .info() + .await + .unwrap() + .faktory + .queues + .get(local) + .is_none()); } #[tokio::test(flavor = "multi_thread")] From 5a174980c68d4a1625d6d0f44312ac26c8dae9b5 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 21 Apr 2024 23:58:30 +0500 Subject: [PATCH 069/129] Support both openssl and rustls --- .github/workflows/tls.yml | 2 +- Cargo.lock | 82 ++++++++++++++ Cargo.toml | 7 +- Makefile | 2 +- src/lib.rs | 3 +- src/tls/mod.rs | 12 +++ src/{tls.rs => tls/openssl.rs} | 5 +- src/tls/rustls.rs | 182 ++++++++++++++++++++++++++++++++ tests/tls/main.rs | 5 + tests/{tls.rs => tls/native.rs} | 5 +- tests/tls/rust.rs | 178 +++++++++++++++++++++++++++++++ 11 files changed, 472 insertions(+), 11 deletions(-) create mode 100644 src/tls/mod.rs rename src/{tls.rs => tls/openssl.rs} (98%) create mode 100644 src/tls/rustls.rs create mode 100644 tests/tls/main.rs rename tests/{tls.rs => tls/native.rs} (97%) create mode 100644 tests/tls/rust.rs diff --git a/.github/workflows/tls.yml b/.github/workflows/tls.yml index 058bf965..11d8106b 100644 --- a/.github/workflows/tls.yml +++ b/.github/workflows/tls.yml @@ -29,4 +29,4 @@ jobs: - name: Run tests env: FAKTORY_URL_SECURE: tcp://localhost:17419 - run: cargo test --locked --features tls --test tls + run: cargo test --locked --features openssl,rustls --test tls diff --git a/Cargo.lock b/Cargo.lock index 34dcb7a4..a8e7ba3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -450,6 +450,7 @@ dependencies = [ "thiserror", "tokio", "tokio-native-tls", + "tokio-rustls", "tokio-test", "url", "x509-parser", @@ -896,6 +897,21 @@ dependencies = [ "getrandom", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -924,6 +940,37 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" + +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.17" @@ -1014,6 +1061,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "strsim" version = "0.10.0" @@ -1026,6 +1079,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -1176,6 +1235,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -1233,6 +1303,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -1501,3 +1577,9 @@ dependencies = [ "thiserror", "time", ] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index 938f6729..5d7f90fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,9 @@ exclude = [".github", "docker", ".gitignore", "Makefile"] [features] default = [] -tls = ["dep:tokio-native-tls", "dep:pin-project"] +tls = ["dep:pin-project"] +openssl = ["tls", "dep:tokio-native-tls"] +rustls = ["tls", "dep:tokio-rustls"] binaries = ["dep:clap", "tokio/macros"] ent = [] @@ -44,6 +46,7 @@ tokio = { version = "1.35.1", features = [ "time", ] } tokio-native-tls = { version = "0.3.1", optional = true } +tokio-rustls = { version = "0.25.0", optional = true } url = "2" [dev-dependencies] @@ -53,7 +56,7 @@ x509-parser = "0.15.1" # to make -Zminimal-versions work [target.'cfg(any())'.dependencies] -openssl = { version = "0.10.60", optional = true } +openssl-crate = { package = "openssl", version = "0.10.60", optional = true } native-tls = { version = "0.2.4", optional = true } num-bigint = "0.4.2" oid-registry = "0.6.1" diff --git a/Makefile b/Makefile index 35cac22a..f8c8ccdd 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ test/e2e: .PHONY: test/e2e/tls test/e2e/tls: FAKTORY_URL_SECURE=tcp://${FAKTORY_HOST}:${FAKTORY_PORT_SECURE} \ - cargo test --locked --features tls --test tls + cargo test --locked --features openssl,rustls --test tls .PHONY: test/load test/load: diff --git a/src/lib.rs b/src/lib.rs index 8f4617e0..d9b3d0e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,5 +91,4 @@ pub mod ent { mod tls; #[cfg(feature = "tls")] -#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] -pub use tls::TlsStream; +pub use tls::*; diff --git a/src/tls/mod.rs b/src/tls/mod.rs new file mode 100644 index 00000000..84c60f05 --- /dev/null +++ b/src/tls/mod.rs @@ -0,0 +1,12 @@ +#[cfg(feature = "openssl")] +#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))] +/// Namespace for OpenSSL-powered [`TlsStream`](crate::openssl::TlsStream). +/// +/// The underlying crate (`native-tls`) will use _SChannel_ on Windows, +/// _SecureTransport_ on OSX, and _OpenSSL_ on other platforms. +pub mod openssl; + +#[cfg(feature = "rustls")] +#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] +/// Namespace for Rustls-powered [`TlsStream`](crate::rustls::TlsStream). +pub mod rustls; diff --git a/src/tls.rs b/src/tls/openssl.rs similarity index 98% rename from src/tls.rs rename to src/tls/openssl.rs index 2a80c690..105d8e29 100644 --- a/src/tls.rs +++ b/src/tls/openssl.rs @@ -19,7 +19,8 @@ use tokio_native_tls::{native_tls::TlsConnector, TlsConnector as AsyncTlsConnect /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{Client, TlsStream}; +/// use faktory::Client; +/// use faktory::openssl::TlsStream; /// let tls = TlsStream::connect(None).await.unwrap(); /// let cl = Client::connect_with(tls, None).await.unwrap(); /// # drop(cl); @@ -31,7 +32,7 @@ pub struct TlsStream { connector: AsyncTlsConnector, hostname: String, #[pin] - stream: NativeTlsStream, + pub(crate) stream: NativeTlsStream, } impl TlsStream { diff --git a/src/tls/rustls.rs b/src/tls/rustls.rs new file mode 100644 index 00000000..c137ff53 --- /dev/null +++ b/src/tls/rustls.rs @@ -0,0 +1,182 @@ +#[cfg(doc)] +use crate::{Client, WorkerBuilder}; + +use crate::{proto::utils, Error, Reconnect}; +use std::fmt::Debug; +use std::io; +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::net::TcpStream as TokioTcpStream; +use tokio_rustls::client::TlsStream as RustlsStream; +use tokio_rustls::rustls::{ClientConfig, RootCertStore}; +use tokio_rustls::TlsConnector; + +/// A reconnectable stream encrypted with TLS. +/// +/// This can be used as an argument to [`WorkerBuilder::connect_with`] and [`Client::connect_with`] to +/// connect to a TLS-secured Faktory server. +/// +/// # Examples +/// +/// ```no_run +/// # tokio_test::block_on(async { +/// use faktory::Client; +/// use faktory::rustls::TlsStream; +/// let tls = TlsStream::connect(None).await.unwrap(); +/// let cl = Client::connect_with(tls, None).await.unwrap(); +/// # drop(cl); +/// # }); +/// ``` +/// +#[pin_project::pin_project] +pub struct TlsStream { + connector: TlsConnector, + hostname: &'static str, + #[pin] + pub(crate) stream: RustlsStream, +} + +impl TlsStream { + /// Create a new TLS connection over TCP. + /// + /// If `url` is not given, will use the standard Faktory environment variables. Specifically, + /// `FAKTORY_PROVIDER` is read to get the name of the environment variable to get the address + /// from (defaults to `FAKTORY_URL`), and then that environment variable is read to get the + /// server address. If the latter environment variable is not defined, the connection will be + /// made to + /// + /// ```text + /// tcp://localhost:7419 + /// ``` + /// + /// If `url` is given, but does not specify a port, it defaults to 7419. + /// + /// Internally creates a `ClientConfig` with an empty root certificates store and no client + /// authentication. Use [`with_client_config`](TlsStream::with_client_config) + /// or [`with_connector`](TlsStream::with_connector) for customized + /// `ClientConfig` and `TlsConnector` accordingly. + pub async fn connect(url: Option<&str>) -> Result { + let conf = ClientConfig::builder() + .with_root_certificates(RootCertStore::empty()) + .with_no_client_auth(); + let con = TlsConnector::from(Arc::new(conf)); + TlsStream::with_connector(con, url).await + } + + /// Create a new asynchronous TLS connection over TCP using a non-default TLS configuration. + /// + /// See `connect` for details about the `url` parameter. + pub async fn with_client_config(conf: ClientConfig, url: Option<&str>) -> Result { + let con = TlsConnector::from(Arc::new(conf)); + TlsStream::with_connector(con, url).await + } + + /// Create a new asynchronous TLS connection over TCP using a connector with a non-default TLS configuration. + /// + /// See `connect` for details about the `url` parameter. + pub async fn with_connector(connector: TlsConnector, url: Option<&str>) -> Result { + let url = match url { + Some(url) => utils::url_parse(url), + None => utils::url_parse(&utils::get_env_url()), + }?; + let hostname = utils::host_from_url(&url); + let tcp_stream = TokioTcpStream::connect(&hostname).await?; + let hostname: &'static str = url.host_str().unwrap().to_string().leak(); + Ok(TlsStream::new(tcp_stream, connector, hostname).await?) + } +} + +impl TlsStream +where + S: AsyncRead + AsyncWrite + Send + Unpin + Reconnect + Debug + 'static, +{ + /// Create a new asynchronous TLS connection on an existing stream. + /// + /// Internally creates a `ClientConfig` with an empty root certificates store and no client + /// authentication. Use [`new`](TlsStream::new) for a customized `TlsConnector`. + pub async fn default(stream: S, hostname: &'static str) -> io::Result { + let conf = ClientConfig::builder() + .with_root_certificates(RootCertStore::empty()) + .with_no_client_auth(); + + Self::new(stream, TlsConnector::from(Arc::new(conf)), hostname).await + } + + /// Create a new asynchronous TLS connection on an existing stream with a non-default TLS configuration. + pub async fn new( + stream: S, + connector: TlsConnector, + hostname: &'static str, + ) -> io::Result { + // let hostname: &'static str = hostname.to_string().leak(); + let domain = hostname.try_into().expect("a valid DNS name or IP address"); + let tls_stream = connector + .connect(domain, stream) + .await + .map_err(|e| io::Error::new(io::ErrorKind::ConnectionAborted, e))?; + Ok(TlsStream { + connector, + hostname, + stream: tls_stream, + }) + } +} + +#[async_trait::async_trait] +impl Reconnect for TlsStream +where + S: AsyncRead + AsyncWrite + Send + Unpin + Reconnect + Debug + 'static + Sync, +{ + async fn reconnect(&mut self) -> io::Result { + let stream = self.stream.get_mut().0.reconnect().await?; + Self::new(stream, self.connector.clone(), &self.hostname).await + } +} + +impl Deref for TlsStream { + type Target = RustlsStream; + fn deref(&self) -> &Self::Target { + &self.stream + } +} + +impl DerefMut for TlsStream { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.stream + } +} + +impl AsyncRead for TlsStream { + fn poll_read( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &mut tokio::io::ReadBuf<'_>, + ) -> std::task::Poll> { + self.project().stream.poll_read(cx, buf) + } +} + +impl AsyncWrite for TlsStream { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + self.project().stream.poll_write(cx, buf) + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.project().stream.poll_flush(cx) + } + + fn poll_shutdown( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.project().stream.poll_shutdown(cx) + } +} diff --git a/tests/tls/main.rs b/tests/tls/main.rs new file mode 100644 index 00000000..32c1cc74 --- /dev/null +++ b/tests/tls/main.rs @@ -0,0 +1,5 @@ +#[cfg(feature = "openssl")] +mod native; + +#[cfg(feature = "rustls")] +mod rust; diff --git a/tests/tls.rs b/tests/tls/native.rs similarity index 97% rename from tests/tls.rs rename to tests/tls/native.rs index d98294f4..7d4378ea 100644 --- a/tests/tls.rs +++ b/tests/tls/native.rs @@ -1,6 +1,5 @@ -#![cfg(feature = "tls")] - -use faktory::{Client, Job, TlsStream, WorkerBuilder}; +use faktory::openssl::TlsStream; +use faktory::{Client, Job, WorkerBuilder}; use serde_json::Value; use std::{env, sync}; diff --git a/tests/tls/rust.rs b/tests/tls/rust.rs new file mode 100644 index 00000000..7fbc825b --- /dev/null +++ b/tests/tls/rust.rs @@ -0,0 +1,178 @@ +use faktory::rustls::TlsStream; +use faktory::{Client, Job, WorkerBuilder}; +use serde_json::Value; +use std::{ + env, + sync::{self, Arc}, +}; +use tokio_rustls::rustls::{ClientConfig, SignatureScheme}; + +#[tokio::test(flavor = "multi_thread")] +async fn roundtrip_tls() { + // We are utilizing the fact that the "FAKTORY_URL_SECURE" environment variable is set + // as an indicator that the integration test can and should be performed. + // + // In case the variable is not set we are returning early. This will show `test ... ok` + // in the test run output, which is admittedly confusing. Ideally, we would like to be able to decorate + // a test with a macro and to see something like `test ... skipped due to `, in case + // the test has been skipped, but it is currently not "natively" supported. + // + // See: https://github.com/rust-lang/rust/issues/68007 + if env::var_os("FAKTORY_URL_SECURE").is_none() { + return; + } + + let local = "roundtrip_tls"; + + let (tx, rx) = sync::mpsc::channel(); + let mut c = WorkerBuilder::default(); + + c.hostname("tester".to_string()).wid(local.into()); + c.register_runner(local, fixtures::JobHandler::new(tx)); + + let tls = || async { + let verifier = fixtures::TestServerCertVerifier::new( + SignatureScheme::RSA_PSS_SHA512, + env::current_dir() + .unwrap() + .join("docker") + .join("certs") + .join("faktory.local.crt"), + ); + let client_config = ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(Arc::new(verifier)) + .with_no_client_auth(); + + TlsStream::with_client_config( + client_config, + Some(&env::var("FAKTORY_URL_SECURE").unwrap()), + ) + .await + .unwrap() + }; + + let mut c = c.connect_with(tls().await, None).await.unwrap(); + let mut p = Client::connect_with(tls().await, None).await.unwrap(); + p.enqueue(Job::new(local, vec!["z"]).on_queue(local)) + .await + .unwrap(); + c.run_one(0, &[local]).await.unwrap(); + + let job = rx.recv().unwrap(); + assert_eq!(job.queue, local); + assert_eq!(job.kind(), local); + assert_eq!(job.args(), &[Value::from("z")]); +} + +mod fixtures { + pub use handler::JobHandler; + pub use tls::TestServerCertVerifier; + + mod handler { + use async_trait::async_trait; + use faktory::{Job, JobRunner}; + use std::{ + io, + sync::{mpsc::Sender, Arc, Mutex}, + time::Duration, + }; + use tokio::time; + + pub struct JobHandler { + chan: Arc>>, + } + + impl JobHandler { + pub fn new(chan: Sender) -> Self { + Self { + chan: Arc::new(Mutex::new(chan)), + } + } + + async fn process_one(&self, job: Job) -> io::Result<()> { + time::sleep(Duration::from_millis(100)).await; + eprintln!("{:?}", job); + self.chan.lock().unwrap().send(job).unwrap(); + Ok(()) + } + } + + #[async_trait] + impl JobRunner for JobHandler { + type Error = io::Error; + + async fn run(&self, job: Job) -> Result<(), Self::Error> { + self.process_one(job).await.unwrap(); + Ok(()) + } + } + } + + mod tls { + #![allow(unused_variables)] + + use std::fs; + use std::path::PathBuf; + + use tokio_rustls::rustls::client::danger::{ + HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, + }; + use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime}; + use tokio_rustls::rustls::DigitallySignedStruct; + use tokio_rustls::rustls::Error as RustlsError; + use tokio_rustls::rustls::SignatureScheme; + use x509_parser::pem::parse_x509_pem; + + #[derive(Debug)] + pub struct TestServerCertVerifier<'a> { + scheme: SignatureScheme, + cert_der: CertificateDer<'a>, + } + + impl TestServerCertVerifier<'_> { + pub fn new(scheme: SignatureScheme, cert_path: PathBuf) -> Self { + let cert = fs::read(&cert_path).unwrap(); + let (_, pem) = parse_x509_pem(&cert).unwrap(); + let cert_der = CertificateDer::try_from(pem.contents).unwrap(); + Self { scheme, cert_der } + } + } + + impl ServerCertVerifier for TestServerCertVerifier<'_> { + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result { + assert_eq!(&self.cert_der, end_entity); + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + Ok(HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![self.scheme] + } + } + } +} From 13eaace0caf25af391e69858758372cc4179b0a7 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 00:26:34 +0500 Subject: [PATCH 070/129] Support both openssl and rustls. Clean up --- .github/workflows/tls.yml | 2 +- Cargo.toml | 7 +++---- Makefile | 2 +- src/error.rs | 5 +++-- src/lib.rs | 4 ++-- src/tls/mod.rs | 8 ++++---- src/tls/{openssl.rs => native_tls.rs} | 2 +- tests/tls/main.rs | 6 +++--- tests/tls/{native.rs => native_tls.rs} | 2 +- tests/tls/{rust.rs => rustls.rs} | 0 10 files changed, 19 insertions(+), 19 deletions(-) rename src/tls/{openssl.rs => native_tls.rs} (99%) rename tests/tls/{native.rs => native_tls.rs} (98%) rename tests/tls/{rust.rs => rustls.rs} (100%) diff --git a/.github/workflows/tls.yml b/.github/workflows/tls.yml index 11d8106b..9c86816b 100644 --- a/.github/workflows/tls.yml +++ b/.github/workflows/tls.yml @@ -29,4 +29,4 @@ jobs: - name: Run tests env: FAKTORY_URL_SECURE: tcp://localhost:17419 - run: cargo test --locked --features openssl,rustls --test tls + run: cargo test --locked --features native_tls,rustls --test tls diff --git a/Cargo.toml b/Cargo.toml index 5d7f90fe..9b654a74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,8 @@ exclude = [".github", "docker", ".gitignore", "Makefile"] [features] default = [] -tls = ["dep:pin-project"] -openssl = ["tls", "dep:tokio-native-tls"] -rustls = ["tls", "dep:tokio-rustls"] +native_tls = ["dep:pin-project", "dep:tokio-native-tls"] +rustls = ["dep:pin-project", "dep:tokio-rustls"] binaries = ["dep:clap", "tokio/macros"] ent = [] @@ -56,7 +55,7 @@ x509-parser = "0.15.1" # to make -Zminimal-versions work [target.'cfg(any())'.dependencies] -openssl-crate = { package = "openssl", version = "0.10.60", optional = true } +openssl = { version = "0.10.60", optional = true } native-tls = { version = "0.2.4", optional = true } num-bigint = "0.4.2" oid-registry = "0.6.1" diff --git a/Makefile b/Makefile index f8c8ccdd..c6b89f8e 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ test/e2e: .PHONY: test/e2e/tls test/e2e/tls: FAKTORY_URL_SECURE=tcp://${FAKTORY_HOST}:${FAKTORY_PORT_SECURE} \ - cargo test --locked --features openssl,rustls --test tls + cargo test --locked --features native_tls,rustls --test tls .PHONY: test/load test/load: diff --git a/src/error.rs b/src/error.rs index bc5e0a7e..40385c78 100644 --- a/src/error.rs +++ b/src/error.rs @@ -43,8 +43,9 @@ pub enum Error { Serialization(#[source] serde_json::Error), /// Indicates an error in the underlying TLS stream. - #[cfg(feature = "tls")] - #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] + + #[cfg(any(feature = "native_tls", feature = "rustls"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "native_tls", feature = "rustls"))))] #[error("underlying tls stream")] TlsStream(#[source] tokio_native_tls::native_tls::Error), } diff --git a/src/lib.rs b/src/lib.rs index d9b3d0e6..b73211f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,8 +87,8 @@ pub mod ent { }; } -#[cfg(feature = "tls")] +#[cfg(any(feature = "native_tls", feature = "rustls"))] mod tls; -#[cfg(feature = "tls")] +#[cfg(any(feature = "native_tls", feature = "rustls"))] pub use tls::*; diff --git a/src/tls/mod.rs b/src/tls/mod.rs index 84c60f05..95cf94c7 100644 --- a/src/tls/mod.rs +++ b/src/tls/mod.rs @@ -1,10 +1,10 @@ -#[cfg(feature = "openssl")] -#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))] -/// Namespace for OpenSSL-powered [`TlsStream`](crate::openssl::TlsStream). +#[cfg(feature = "native_tls")] +#[cfg_attr(docsrs, doc(cfg(feature = "native_tls")))] +/// Namespace for native TLS powered [`TlsStream`](crate::native_tls::TlsStream). /// /// The underlying crate (`native-tls`) will use _SChannel_ on Windows, /// _SecureTransport_ on OSX, and _OpenSSL_ on other platforms. -pub mod openssl; +pub mod native_tls; #[cfg(feature = "rustls")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] diff --git a/src/tls/openssl.rs b/src/tls/native_tls.rs similarity index 99% rename from src/tls/openssl.rs rename to src/tls/native_tls.rs index 105d8e29..a66dc49b 100644 --- a/src/tls/openssl.rs +++ b/src/tls/native_tls.rs @@ -20,7 +20,7 @@ use tokio_native_tls::{native_tls::TlsConnector, TlsConnector as AsyncTlsConnect /// ```no_run /// # tokio_test::block_on(async { /// use faktory::Client; -/// use faktory::openssl::TlsStream; +/// use faktory::native_tls::TlsStream; /// let tls = TlsStream::connect(None).await.unwrap(); /// let cl = Client::connect_with(tls, None).await.unwrap(); /// # drop(cl); diff --git a/tests/tls/main.rs b/tests/tls/main.rs index 32c1cc74..5369ef6a 100644 --- a/tests/tls/main.rs +++ b/tests/tls/main.rs @@ -1,5 +1,5 @@ -#[cfg(feature = "openssl")] -mod native; +#[cfg(feature = "native_tls")] +mod native_tls; #[cfg(feature = "rustls")] -mod rust; +mod rustls; diff --git a/tests/tls/native.rs b/tests/tls/native_tls.rs similarity index 98% rename from tests/tls/native.rs rename to tests/tls/native_tls.rs index 7d4378ea..05957642 100644 --- a/tests/tls/native.rs +++ b/tests/tls/native_tls.rs @@ -1,4 +1,4 @@ -use faktory::openssl::TlsStream; +use faktory::native_tls::TlsStream; use faktory::{Client, Job, WorkerBuilder}; use serde_json::Value; use std::{env, sync}; diff --git a/tests/tls/rust.rs b/tests/tls/rustls.rs similarity index 100% rename from tests/tls/rust.rs rename to tests/tls/rustls.rs From 8d54b0c8c3860e77eb963b3f1e8b369688b5c406 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 00:28:01 +0500 Subject: [PATCH 071/129] Support both openssl and rustls. Min versions fix --- Cargo.lock | 1 + Cargo.toml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8e7ba3f..b2ae48b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,6 +443,7 @@ dependencies = [ "openssl", "pin-project", "rand", + "rustls", "serde", "serde_derive", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 9b654a74..ea164c0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,11 +55,12 @@ x509-parser = "0.15.1" # to make -Zminimal-versions work [target.'cfg(any())'.dependencies] -openssl = { version = "0.10.60", optional = true } +chrono = "0.4.32" native-tls = { version = "0.2.4", optional = true } num-bigint = "0.4.2" oid-registry = "0.6.1" -chrono = "0.4.32" +openssl = { version = "0.10.60", optional = true } +rustls = "0.22.1" [[bin]] name = "loadtest" From f1e5966b7fd319b457e39f764ff712a2cd3d0ad8 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 01:26:31 +0500 Subject: [PATCH 072/129] Support both openssl and rustls. Add TlsStream error --- src/error.rs | 22 +++++++++++++++++++--- src/tls/native_tls.rs | 12 ++++++++---- src/tls/rustls.rs | 14 ++++++-------- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/error.rs b/src/error.rs index 40385c78..13043243 100644 --- a/src/error.rs +++ b/src/error.rs @@ -43,11 +43,10 @@ pub enum Error { Serialization(#[source] serde_json::Error), /// Indicates an error in the underlying TLS stream. - #[cfg(any(feature = "native_tls", feature = "rustls"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "native_tls", feature = "rustls"))))] - #[error("underlying tls stream")] - TlsStream(#[source] tokio_native_tls::native_tls::Error), + #[error("tls stream")] + TlsStream(#[from] TlsStream), } /// Errors specific to connection logic. @@ -161,3 +160,20 @@ impl Protocol { } } } + +/// Implementation specific errors in the underlying TLS stream. +#[derive(Debug, Error)] +#[non_exhaustive] +pub enum TlsStream { + /// Error in the underlying native tls powered stream. + #[cfg(feature = "native_tls")] + #[cfg_attr(docsrs, doc(cfg(feature = "native_tls")))] + #[error("underlying tls stream")] + Native(#[source] tokio_native_tls::native_tls::Error), + + /// Error in the underlying rutsls powered stream. + #[cfg(feature = "rustls")] + #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] + #[error("underlying tls stream")] + Rustls(#[source] tokio_rustls::rustls::Error), +} diff --git a/src/tls/native_tls.rs b/src/tls/native_tls.rs index a66dc49b..95273a88 100644 --- a/src/tls/native_tls.rs +++ b/src/tls/native_tls.rs @@ -1,7 +1,9 @@ #[cfg(doc)] use crate::{Client, WorkerBuilder}; -use crate::{proto::utils, Error, Reconnect}; +use crate::error::{self, Error}; +use crate::proto::utils; +use crate::Reconnect; use std::fmt::Debug; use std::io; use std::ops::{Deref, DerefMut}; @@ -32,7 +34,7 @@ pub struct TlsStream { connector: AsyncTlsConnector, hostname: String, #[pin] - pub(crate) stream: NativeTlsStream, + stream: NativeTlsStream, } impl TlsStream { @@ -51,7 +53,9 @@ impl TlsStream { /// If `url` is given, but does not specify a port, it defaults to 7419. pub async fn connect(url: Option<&str>) -> Result { TlsStream::with_connector( - TlsConnector::builder().build().map_err(Error::TlsStream)?, + TlsConnector::builder() + .build() + .map_err(error::TlsStream::Native)?, url, ) .await @@ -83,7 +87,7 @@ where pub async fn default(stream: S, hostname: &str) -> io::Result { let connector = TlsConnector::builder() .build() - .map_err(Error::TlsStream) + .map_err(error::TlsStream::Native) .unwrap(); Self::new(stream, connector, hostname).await } diff --git a/src/tls/rustls.rs b/src/tls/rustls.rs index c137ff53..adbc2b0f 100644 --- a/src/tls/rustls.rs +++ b/src/tls/rustls.rs @@ -34,7 +34,7 @@ pub struct TlsStream { connector: TlsConnector, hostname: &'static str, #[pin] - pub(crate) stream: RustlsStream, + stream: RustlsStream, } impl TlsStream { @@ -64,7 +64,7 @@ impl TlsStream { TlsStream::with_connector(con, url).await } - /// Create a new asynchronous TLS connection over TCP using a non-default TLS configuration. + /// Create a new TLS connection over TCP using a non-default TLS configuration. /// /// See `connect` for details about the `url` parameter. pub async fn with_client_config(conf: ClientConfig, url: Option<&str>) -> Result { @@ -72,7 +72,7 @@ impl TlsStream { TlsStream::with_connector(con, url).await } - /// Create a new asynchronous TLS connection over TCP using a connector with a non-default TLS configuration. + /// Create a new TLS connection over TCP using a connector with a non-default TLS configuration. /// /// See `connect` for details about the `url` parameter. pub async fn with_connector(connector: TlsConnector, url: Option<&str>) -> Result { @@ -82,8 +82,7 @@ impl TlsStream { }?; let hostname = utils::host_from_url(&url); let tcp_stream = TokioTcpStream::connect(&hostname).await?; - let hostname: &'static str = url.host_str().unwrap().to_string().leak(); - Ok(TlsStream::new(tcp_stream, connector, hostname).await?) + Ok(TlsStream::new(tcp_stream, connector, hostname.leak()).await?) } } @@ -91,7 +90,7 @@ impl TlsStream where S: AsyncRead + AsyncWrite + Send + Unpin + Reconnect + Debug + 'static, { - /// Create a new asynchronous TLS connection on an existing stream. + /// Create a new TLS connection on an existing stream. /// /// Internally creates a `ClientConfig` with an empty root certificates store and no client /// authentication. Use [`new`](TlsStream::new) for a customized `TlsConnector`. @@ -103,13 +102,12 @@ where Self::new(stream, TlsConnector::from(Arc::new(conf)), hostname).await } - /// Create a new asynchronous TLS connection on an existing stream with a non-default TLS configuration. + /// Create a new TLS connection on an existing stream with a non-default TLS configuration. pub async fn new( stream: S, connector: TlsConnector, hostname: &'static str, ) -> io::Result { - // let hostname: &'static str = hostname.to_string().leak(); let domain = hostname.try_into().expect("a valid DNS name or IP address"); let tls_stream = connector .connect(domain, stream) From 58cb247ee9974557dc08d133295c1b0b7572ffe9 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 16:18:22 +0500 Subject: [PATCH 073/129] Support both openssl and rustls. Rustls clean up --- Makefile | 2 +- src/tls/rustls.rs | 27 ++++++++++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index c6b89f8e..a1dc59e5 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,7 @@ test/e2e: .PHONY: test/e2e/tls test/e2e/tls: FAKTORY_URL_SECURE=tcp://${FAKTORY_HOST}:${FAKTORY_PORT_SECURE} \ - cargo test --locked --features native_tls,rustls --test tls + cargo test --locked --features native_tls,rustls --test tls -- --nocapture .PHONY: test/load test/load: diff --git a/src/tls/rustls.rs b/src/tls/rustls.rs index adbc2b0f..ba42520b 100644 --- a/src/tls/rustls.rs +++ b/src/tls/rustls.rs @@ -80,9 +80,10 @@ impl TlsStream { Some(url) => utils::url_parse(url), None => utils::url_parse(&utils::get_env_url()), }?; - let hostname = utils::host_from_url(&url); - let tcp_stream = TokioTcpStream::connect(&hostname).await?; - Ok(TlsStream::new(tcp_stream, connector, hostname.leak()).await?) + let host_and_port = utils::host_from_url(&url); + let tcp_stream = TokioTcpStream::connect(&host_and_port).await?; + let host = url.host_str().unwrap(); + Ok(TlsStream::new(tcp_stream, connector, host).await?) } } @@ -106,11 +107,23 @@ where pub async fn new( stream: S, connector: TlsConnector, - hostname: &'static str, + hostname: impl Into, ) -> io::Result { - let domain = hostname.try_into().expect("a valid DNS name or IP address"); + let hostname: &'static str = hostname.into().leak(); + TlsStream::create_new(stream, connector, hostname).await + } + + /// Actually create new `TlsStream`. + /// + /// This private faktory method is needed to be able re-use the already `&'static str` hostname + /// when re-connecting, rather than allocate new String and leak it yet again. + /// + /// See how we are leaking the `hostname` in [`new`](TlsStream::new) constructor. This is needed + /// to satisfy the `tokio_rustls::TlsConnector::connect` which is expecting a `pki_types::ServerName<'static>`. + async fn create_new(stream: S, connector: TlsConnector, hostname: &'static str) -> io::Result{ + let server_name = hostname.try_into().expect("a valid DNS name or IP address"); let tls_stream = connector - .connect(domain, stream) + .connect(server_name, stream) .await .map_err(|e| io::Error::new(io::ErrorKind::ConnectionAborted, e))?; Ok(TlsStream { @@ -128,7 +141,7 @@ where { async fn reconnect(&mut self) -> io::Result { let stream = self.stream.get_mut().0.reconnect().await?; - Self::new(stream, self.connector.clone(), &self.hostname).await + TlsStream::create_new(stream, self.connector.clone(), self.hostname).await } } From 6628efb408067adb7e6c3fed9741b45e64629f88 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 16:25:28 +0500 Subject: [PATCH 074/129] Rm `async` from loadtest binary name --- src/bin/loadtest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index 5dbc5054..14eefc86 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -12,7 +12,7 @@ const QUEUES: &[&str] = &["queue0", "queue1", "queue2", "queue3", "queue4"]; #[tokio::main] async fn main() { - let matches = Command::new("My Super Program (Async)") + let matches = Command::new("My Super Program") .version("0.1") .about("Benchmark the performance of Rust Faktory async workers and client") .arg( From 6af3dcc30499c5ba3dbbaddbcd0fdbc291527791 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 16:43:51 +0500 Subject: [PATCH 075/129] Rm leading underscore in `ops_count` var in loadtest --- src/bin/loadtest.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index 14eefc86..b3537e62 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -99,9 +99,9 @@ async fn main() { }); } - let mut _ops_count = Vec::with_capacity(threads); + let mut ops_count = Vec::with_capacity(threads); while let Some(res) = set.join_next().await { - _ops_count.push(res.unwrap()) + ops_count.push(res.unwrap()) } let stop = start.elapsed(); @@ -115,5 +115,5 @@ async fn main() { stop_secs, jobs as f64 / stop_secs, ); - println!("{:?}", _ops_count); + println!("Number of operations (pushes and pops) per thread: {:?}", ops_count); } From f8d3f3208dea625fa25e3a71ec2d047905b7e83b Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 18:22:39 +0500 Subject: [PATCH 076/129] Rm redundant 'asynchronously' from the docs in proto::client mod --- src/proto/client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 9a0b1efb..947aafef 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -319,7 +319,7 @@ impl Client where S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, { - /// Asynchronously enqueue the given job on the Faktory server. + /// Enqueue the given job on the Faktory server. /// /// Returns `Ok` if the job was successfully queued by the Faktory server. pub async fn enqueue(&mut self, job: Job) -> Result<(), Error> { From e9cd5813aee306f4009c5e34522289f33ac18dc5 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 18:37:36 +0500 Subject: [PATCH 077/129] Update private docs for ClientOptions --- src/proto/client/options.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/proto/client/options.rs b/src/proto/client/options.rs index 98fa58ae..f13c8a82 100644 --- a/src/proto/client/options.rs +++ b/src/proto/client/options.rs @@ -2,22 +2,32 @@ use crate::proto::WorkerId; #[derive(Clone, Debug)] pub(crate) struct ClientOptions { - // Hostname to advertise to server. Defaults to machine hostname. + /// Hostname to advertise to server. + /// + /// Defaults to machine hostname. pub(crate) hostname: Option, - // PID to advertise to server. Defaults to process ID. + /// PID to advertise to server. + /// + /// Defaults to process ID. pub(crate) pid: Option, - // Worker ID to advertise to server Defaults to a GUID. + /// Worker ID to advertise to server. + /// + /// Defaults to a GUID. pub(crate) wid: Option, - // Labels to advertise to server. Defaults to ["rust"]. + /// Labels to advertise to server. + /// + /// Defaults to ["rust"]. pub(crate) labels: Vec, - // Password to authenticate with. Defaults to None. + /// Password to authenticate with. + /// + /// Defaults to None. pub(crate) password: Option, - // Whether this client is instatianted for a worker (i.e. to consume jobs). + /// Whether this client is instatianted for a worker (i.e. to consume jobs). pub(crate) is_worker: bool, } From 2df103dd59551d2db758bad8c2ecaffe4cc1f86a Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 20:00:27 +0500 Subject: [PATCH 078/129] Craete BatchId, WorkerId, and JobId with `::new` --- src/proto/batch/mod.rs | 4 +++- src/proto/single/id.rs | 14 ++++++++------ src/proto/single/mod.rs | 2 +- src/proto/single/resp.rs | 9 +++++---- tests/consumer.rs | 10 +++++----- tests/real/community.rs | 23 +++++++++++++---------- tests/real/enterprise.rs | 2 +- tests/tls/native_tls.rs | 4 ++-- tests/tls/rustls.rs | 4 ++-- 9 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/proto/batch/mod.rs b/src/proto/batch/mod.rs index 52d7d617..8845fa9f 100644 --- a/src/proto/batch/mod.rs +++ b/src/proto/batch/mod.rs @@ -220,6 +220,8 @@ mod test { use chrono::{DateTime, Utc}; + use crate::JobId; + use super::*; #[test] @@ -255,7 +257,7 @@ mod test { #[test] fn test_batch_serialized_correctly() { let prepare_test_job = |jobtype: String| { - let jid = "LFluKy1Baak83p54"; + let jid = JobId::new("LFluKy1Baak83p54"); let dt = "2023-12-22T07:00:52.546258624Z"; let created_at = DateTime::::from_str(dt).unwrap(); Job::builder(jobtype) diff --git a/src/proto/single/id.rs b/src/proto/single/id.rs index fada2bfe..00f10386 100644 --- a/src/proto/single/id.rs +++ b/src/proto/single/id.rs @@ -1,14 +1,16 @@ use super::utils; use std::ops::{Deref, DerefMut}; +use std::fmt::Display; macro_rules! string_wrapper_impls { ($new_type:ident) => { - impl From for $new_type - where - S: AsRef, - { - fn from(value: S) -> Self { - $new_type(value.as_ref().to_owned()) + impl $new_type { + /// Document me! + pub fn new(inner: S) -> Self + where + S: AsRef + Clone + Display, + { + Self(inner.to_string()) } } diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index c7f1a993..fce78470 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -289,7 +289,7 @@ mod test { let job_args = vec!["ISBN-13:9781718501850"]; let job = JobBuilder::new(job_kind).args(job_args.clone()).build(); - assert!(job.jid != "".into()); + assert!(job.jid != JobId::new("")); assert!(job.queue == JOB_DEFAULT_QUEUE.to_string()); assert_eq!(job.kind, job_kind); assert_eq!(job.args, job_args); diff --git a/src/proto/single/resp.rs b/src/proto/single/resp.rs index 18e5a9b1..bd0a07b9 100644 --- a/src/proto/single/resp.rs +++ b/src/proto/single/resp.rs @@ -72,12 +72,13 @@ pub async fn read_bid(r: R) -> Result Ok(std::str::from_utf8(b) - .map_err(|_| error::Protocol::BadType { + RawResponse::Blob(ref b) => { + let raw = std::str::from_utf8(b).map_err(|_| error::Protocol::BadType { expected: "valid blob representation of batch id", received: "unprocessable blob".into(), - })? - .into()), + })?; + Ok(BatchId::new(raw)) + } something_else => Err(bad("id", &something_else).into()), } } diff --git a/tests/consumer.rs b/tests/consumer.rs index 8aab27db..bf050ec4 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -9,7 +9,7 @@ async fn hello() { let mut s = mock::Stream::default(); let mut c: WorkerBuilder = WorkerBuilder::default(); c.hostname("host".to_string()) - .wid("wid".into()) + .wid(WorkerId::new("wid")) .labels(vec!["foo".to_string(), "bar".to_string()]); c.register("never_called", |_j: Job| async move { unreachable!() }); let c = c.connect_with(s.clone(), None).await.unwrap(); @@ -152,7 +152,7 @@ async fn dequeue_first_empty() { async fn well_behaved() { let mut s = mock::Stream::new(2); // main plus worker let mut c = WorkerBuilder::default(); - c.wid("wid".into()); + c.wid(WorkerId::new("wid")); c.register("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; @@ -217,7 +217,7 @@ async fn well_behaved() { async fn no_first_job() { let mut s = mock::Stream::new(2); let mut c = WorkerBuilder::default(); - c.wid("wid".into()); + c.wid(WorkerId::new("wid")); c.register("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; @@ -284,7 +284,7 @@ async fn well_behaved_many() { let mut s = mock::Stream::new(3); let mut c = WorkerBuilder::default(); c.workers(2); - c.wid("wid".into()); + c.wid(WorkerId::new("wid")); c.register("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; @@ -359,7 +359,7 @@ async fn well_behaved_many() { async fn terminate() { let mut s = mock::Stream::new(2); let mut c: WorkerBuilder = WorkerBuilder::default(); - c.wid("wid".into()); + c.wid(WorkerId::new("wid")); c.register("foobar", |_| async move { loop { sleep(Duration::from_secs(5)).await; diff --git a/tests/real/community.rs b/tests/real/community.rs index d8b36885..77aa225c 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -1,5 +1,5 @@ use crate::skip_check; -use faktory::{Client, Job, JobBuilder, WorkerBuilder}; +use faktory::{Client, Job, JobBuilder, JobId, WorkerBuilder, WorkerId}; use serde_json::Value; use std::{io, sync}; @@ -33,7 +33,7 @@ async fn roundtrip() { skip_check!(); let local = "roundtrip"; - let jid = String::from("x-job-id-0123456782"); + let jid = JobId::new("x-job-id-0123456782"); let mut c = WorkerBuilder::default(); c.register("order", move |job| async move { @@ -47,7 +47,7 @@ async fn roundtrip() { let mut p = Client::connect(None).await.unwrap(); p.enqueue( JobBuilder::new("order") - .jid(&jid) + .jid(jid) .args(vec!["ISBN-13:9781718501850"]) .queue(local) .build(), @@ -69,7 +69,7 @@ async fn multi() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local.into()); + c.hostname("tester".to_string()).wid(WorkerId::new(local)); c.register(local, move |j| { let tx = sync::Arc::clone(&tx); @@ -110,7 +110,7 @@ async fn fail() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local.into()); + c.hostname("tester".to_string()).wid(WorkerId::new(local)); c.register(local, move |j| { let tx = sync::Arc::clone(&tx); @@ -147,7 +147,7 @@ async fn queue() { let tx = sync::Arc::new(sync::Mutex::new(tx)); let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local.into()); + c.hostname("tester".to_string()).wid(WorkerId::new(local)); c.register(local, move |_job| { let tx = sync::Arc::clone(&tx); Box::pin(async move { tx.lock().unwrap().send(true) }) @@ -203,13 +203,16 @@ async fn test_jobs_pushed_in_bulk() { let (enqueued_count, errors) = p .enqueue_many([ - Job::builder("broken").jid("short").queue(local_3).build(), // jid.len() < 8 + Job::builder("broken") + .jid(JobId::new("short")) + .queue(local_3) + .build(), // jid.len() < 8 Job::builder("") // empty string jobtype - .jid("3sZCbdp8e9WX__0") + .jid(JobId::new("3sZCbdp8e9WX__0")) .queue(local_3) .build(), Job::builder("broken") - .jid("3sZCbdp8e9WX__1") + .jid(JobId::new("3sZCbdp8e9WX__1")) .queue(local_3) .reserve_for(864001) // reserve_for exceeded .build(), @@ -241,7 +244,7 @@ async fn test_jobs_pushed_in_bulk() { // have _really_ been enqueued, i.e. that `enqueue_many` // is not an all-or-nothing operation: let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local_3.into()); + c.hostname("tester".to_string()).wid(WorkerId::new(local_3)); c.register("very_special", move |_job| async { Ok::<(), io::Error>(()) }); diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index 74bb5ff0..cb81d69d 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -998,7 +998,7 @@ async fn test_batch_can_be_reopened_add_extra_jobs_and_batches_added() { // ############################## SUBTEST 0 ########################################## // Let's try to open/reopen a batch we have never declared: let b = p - .open_batch(BatchId::from("non-existent-batch-id")) + .open_batch(BatchId::new("non-existent-batch-id")) .await .unwrap(); // The server will error back on this, with "No such batch ", but diff --git a/tests/tls/native_tls.rs b/tests/tls/native_tls.rs index 05957642..19263fc5 100644 --- a/tests/tls/native_tls.rs +++ b/tests/tls/native_tls.rs @@ -1,5 +1,5 @@ use faktory::native_tls::TlsStream; -use faktory::{Client, Job, WorkerBuilder}; +use faktory::{Client, Job, WorkerBuilder, WorkerId}; use serde_json::Value; use std::{env, sync}; @@ -25,7 +25,7 @@ async fn roundtrip_tls() { let (tx, rx) = sync::mpsc::channel(); let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local.into()); + c.hostname("tester".to_string()).wid(WorkerId::new(local)); c.register_runner(local, fixtures::JobHandler::new(tx)); let tls = || async { diff --git a/tests/tls/rustls.rs b/tests/tls/rustls.rs index 7fbc825b..8885d353 100644 --- a/tests/tls/rustls.rs +++ b/tests/tls/rustls.rs @@ -1,5 +1,5 @@ use faktory::rustls::TlsStream; -use faktory::{Client, Job, WorkerBuilder}; +use faktory::{Client, Job, WorkerBuilder, WorkerId}; use serde_json::Value; use std::{ env, @@ -27,7 +27,7 @@ async fn roundtrip_tls() { let (tx, rx) = sync::mpsc::channel(); let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(local.into()); + c.hostname("tester".to_string()).wid(WorkerId::new(local)); c.register_runner(local, fixtures::JobHandler::new(tx)); let tls = || async { From a4919649b4697027c40cdf351b00184b90db75db Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 20:48:46 +0500 Subject: [PATCH 079/129] Add `AsRef` impl for BatchId, WorkerId, and JobId --- src/proto/single/id.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/proto/single/id.rs b/src/proto/single/id.rs index 00f10386..9bb7b57c 100644 --- a/src/proto/single/id.rs +++ b/src/proto/single/id.rs @@ -1,11 +1,11 @@ use super::utils; -use std::ops::{Deref, DerefMut}; use std::fmt::Display; +use std::ops::Deref; macro_rules! string_wrapper_impls { ($new_type:ident) => { impl $new_type { - /// Document me! + /// Create a new entity identifier. pub fn new(inner: S) -> Self where S: AsRef + Clone + Display, @@ -21,9 +21,9 @@ macro_rules! string_wrapper_impls { } } - impl DerefMut for $new_type { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 + impl AsRef for $new_type { + fn as_ref(&self) -> &str { + self.deref().as_ref() } } }; From bb60f6bc9044bcabcfe871067212d7606b7830f4 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 22 Apr 2024 20:51:53 +0500 Subject: [PATCH 080/129] Run formatter. Fix doc code --- src/bin/loadtest.rs | 5 ++++- src/proto/client/mod.rs | 6 +++--- src/proto/client/options.rs | 4 ++-- src/tls/rustls.rs | 10 +++++++--- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index b3537e62..bdeccc17 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -115,5 +115,8 @@ async fn main() { stop_secs, jobs as f64 / stop_secs, ); - println!("Number of operations (pushes and pops) per thread: {:?}", ops_count); + println!( + "Number of operations (pushes and pops) per thread: {:?}", + ops_count + ); } diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 947aafef..f2d3ade6 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -104,7 +104,7 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// ```no_run /// # tokio_test::block_on(async { /// use faktory::{Client, JobId, ent::JobState}; -/// let job_id = JobId::from("W8qyVle9vXzUWQOf"); +/// let job_id = JobId::new("W8qyVle9vXzUWQOf"); /// let mut cl = Client::connect(None).await?; /// if let Some(progress) = cl.get_progress(job_id).await? { /// if let JobState::Success = progress.state { @@ -122,7 +122,7 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// ```no_run /// # tokio_test::block_on(async { /// use faktory::{Client, JobId, ent::ProgressUpdateBuilder}; -/// let jid = JobId::from("W8qyVle9vXzUWQOf"); +/// let jid = JobId::new("W8qyVle9vXzUWQOf"); /// let mut cl = Client::connect(None).await?; /// let progress = ProgressUpdateBuilder::new(jid) /// .desc("Almost done...".to_owned()) @@ -138,7 +138,7 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// ```no_run /// # tokio_test::block_on(async { /// use faktory::{Client, ent::BatchId}; -/// let bid = BatchId::from("W8qyVle9vXzUWQOg"); +/// let bid = BatchId::new("W8qyVle9vXzUWQOg"); /// let mut cl = Client::connect(None).await?; /// if let Some(status) = cl.get_batch_status(bid).await? { /// println!("This batch created at {}", status.created_at); diff --git a/src/proto/client/options.rs b/src/proto/client/options.rs index f13c8a82..aaa5e269 100644 --- a/src/proto/client/options.rs +++ b/src/proto/client/options.rs @@ -13,7 +13,7 @@ pub(crate) struct ClientOptions { pub(crate) pid: Option, /// Worker ID to advertise to server. - /// + /// /// Defaults to a GUID. pub(crate) wid: Option, @@ -23,7 +23,7 @@ pub(crate) struct ClientOptions { pub(crate) labels: Vec, /// Password to authenticate with. - /// + /// /// Defaults to None. pub(crate) password: Option, diff --git a/src/tls/rustls.rs b/src/tls/rustls.rs index ba42520b..24f7cba2 100644 --- a/src/tls/rustls.rs +++ b/src/tls/rustls.rs @@ -114,13 +114,17 @@ where } /// Actually create new `TlsStream`. - /// + /// /// This private faktory method is needed to be able re-use the already `&'static str` hostname /// when re-connecting, rather than allocate new String and leak it yet again. - /// + /// /// See how we are leaking the `hostname` in [`new`](TlsStream::new) constructor. This is needed /// to satisfy the `tokio_rustls::TlsConnector::connect` which is expecting a `pki_types::ServerName<'static>`. - async fn create_new(stream: S, connector: TlsConnector, hostname: &'static str) -> io::Result{ + async fn create_new( + stream: S, + connector: TlsConnector, + hostname: &'static str, + ) -> io::Result { let server_name = hostname.try_into().expect("a valid DNS name or IP address"); let tls_stream = connector .connect(server_name, stream) From 14bcf64c4d4455e6fbe6f86389a2dc9cedf53b19 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 12:15:52 +0500 Subject: [PATCH 081/129] Take 'Fail' in WorkerStatesRegistry::register_failure --- src/worker/mod.rs | 2 +- src/worker/state.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/worker/mod.rs b/src/worker/mod.rs index 899ff556..ed550445 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -265,7 +265,7 @@ impl Fail::generic(jid, format!("No handler for {}", jt)), Failed::Application(e) => Fail::generic_with_backtrace(jid, e), }; - self.worker_states.register_failure(worker, &fail); + self.worker_states.register_failure(worker, fail.clone()); self.report_failure_to_server(&fail).await?; } } diff --git a/src/worker/state.rs b/src/worker/state.rs index 0fb9fe0b..870076bc 100644 --- a/src/worker/state.rs +++ b/src/worker/state.rs @@ -55,11 +55,11 @@ impl WorkerStatesRegistry { .save_last_result(Ok(jid)); } - pub(crate) fn register_failure(&self, worker: usize, f: &Fail) { + pub(crate) fn register_failure(&self, worker: usize, f: Fail) { self[worker] .lock() .expect("lock acquired") - .save_last_result(Err(f.clone())); + .save_last_result(Err(f)); } pub(crate) fn reset(&self, worker: usize) { From 609950fc9f528021b0c27bd4ede8c7d91be45f19 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 15:00:35 +0500 Subject: [PATCH 082/129] Impl IntoIterator for WorkerStatesRegistry --- src/worker/mod.rs | 4 ++-- src/worker/state.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/worker/mod.rs b/src/worker/mod.rs index ed550445..83c0d3b6 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -188,7 +188,7 @@ impl usize { let mut running = 0; - for wstate in self.worker_states.iter() { + for wstate in &*self.worker_states { let may_be_jid = wstate.lock().unwrap().take_cuurently_running(); if let Some(jid) = may_be_jid { running += 1; diff --git a/src/worker/state.rs b/src/worker/state.rs index 870076bc..78f17d20 100644 --- a/src/worker/state.rs +++ b/src/worker/state.rs @@ -39,6 +39,24 @@ impl DerefMut for WorkerStatesRegistry { } } +impl<'a> IntoIterator for &'a WorkerStatesRegistry { + type Item = &'a Mutex; + type IntoIter = <&'a Vec> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a> IntoIterator for &'a mut WorkerStatesRegistry { + type Item = &'a mut Mutex; + type IntoIter = <&'a mut Vec> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + impl WorkerStatesRegistry { pub(crate) fn new(workers_count: usize) -> Self { Self((0..workers_count).map(|_| Default::default()).collect()) From db9b636abb07be0f348d810591f1b75821d599c7 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 15:26:38 +0500 Subject: [PATCH 083/129] Add docs for Closure newtype --- src/worker/runner.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/worker/runner.rs b/src/worker/runner.rs index 0bae806a..a3cecb44 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -77,6 +77,18 @@ where } } +/// A wrapper for the userland's handler. +/// +/// The `Closure` newtype is introduced to avoid having to box a job handler: +/// we can now use `Closure(handler)` instead of `Box::new(handler)` and make +/// the compiler happy. +/// +/// The `repr(transparent)` macro is to guarantee that this single-field struct +/// and the wrapped handler have the same layout and so it is safe to operate on +/// the in-memory representations of _the_ handler (submitted to us +/// from the userland) and its enclosed (by us) self. +/// +/// Ref: https://github.com/jonhoo/faktory-rs/pull/51 #[repr(transparent)] pub(crate) struct Closure(pub F); From 757f92e68d5d9ca9250a83ca57b7c63dc529bd8d Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 16:49:50 +0500 Subject: [PATCH 084/129] Restore JobRunner impl for &' mut F --- src/worker/runner.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/worker/runner.rs b/src/worker/runner.rs index a3cecb44..f3f12268 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -77,6 +77,18 @@ where } } +#[async_trait::async_trait] +impl<'a, E, F, Fut> JobRunner for &'a mut F +where + F: Send + Sync + Fn(Job) -> Fut, + Fut: Future> + Send, +{ + type Error = E; + async fn run(&self, job: Job) -> Result<(), E> { + (self as &F)(job).await + } +} + /// A wrapper for the userland's handler. /// /// The `Closure` newtype is introduced to avoid having to box a job handler: From 6469573adf1b03d91a538886f7d9fd9bdc54894c Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 18:46:00 +0500 Subject: [PATCH 085/129] Update worker in community::roundtrip test to chain `register` --- tests/real/community.rs | 47 +++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/tests/real/community.rs b/tests/real/community.rs index 77aa225c..f9bdb208 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -35,29 +35,34 @@ async fn roundtrip() { let local = "roundtrip"; let jid = JobId::new("x-job-id-0123456782"); - let mut c = WorkerBuilder::default(); - c.register("order", move |job| async move { - assert_eq!(job.kind(), "order"); - assert_eq!(job.queue, local); - assert_eq!(job.args(), &[Value::from("ISBN-13:9781718501850")]); - Ok::<(), io::Error>(()) - }); - c.register("image", |_| async move { unreachable!() }); - let mut c = c.connect(None).await.unwrap(); - let mut p = Client::connect(None).await.unwrap(); - p.enqueue( - JobBuilder::new("order") - .jid(jid) - .args(vec!["ISBN-13:9781718501850"]) - .queue(local) - .build(), - ) - .await - .unwrap(); - let had_one = c.run_one(0, &[local]).await.unwrap(); + let mut worker = WorkerBuilder::default(); + worker + .labels(vec!["rust".into(), local.into()]) + .workers(1) + .wid(WorkerId::random()) + .register("order", move |job| async move { + assert_eq!(job.kind(), "order"); + assert_eq!(job.queue, local); + assert_eq!(job.args(), &[Value::from("ISBN-13:9781718501850")]); + Ok::<(), io::Error>(()) + }) + .register("image", |_| async move { unreachable!() }); + let mut worker = worker.connect(None).await.unwrap(); + let mut client = Client::connect(None).await.unwrap(); + client + .enqueue( + JobBuilder::new("order") + .jid(jid) + .args(vec!["ISBN-13:9781718501850"]) + .queue(local) + .build(), + ) + .await + .unwrap(); + let had_one = worker.run_one(0, &[local]).await.unwrap(); assert!(had_one); - let drained = !c.run_one(0, &[local]).await.unwrap(); + let drained = !worker.run_one(0, &[local]).await.unwrap(); assert!(drained); } From 803400d6b6940b7bdbabd8859c9b8f1fe4359df8 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 18:48:17 +0500 Subject: [PATCH 086/129] Do not requiere *Ext in src::worker::mod --- src/worker/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/worker/mod.rs b/src/worker/mod.rs index 83c0d3b6..be3a4cab 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -4,7 +4,7 @@ use crate::proto::{Ack, Fail, Job, JobId}; use fnv::FnvHashMap; use std::sync::{atomic, Arc}; use std::{error::Error as StdError, sync::atomic::AtomicUsize}; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::io::{AsyncBufRead, AsyncWrite}; use tokio::task::JoinHandle; mod builder; @@ -136,20 +136,20 @@ type CallbacksRegistry = FnvHashMap>; /// You can also register anything that implements [`JobRunner`] to handle jobs /// with [`register_runner`](WorkerBuilder::register_runner). /// -pub struct Worker { +pub struct Worker { c: Client, worker_states: Arc, callbacks: Arc>, terminated: bool, } -impl Worker { +impl Worker { async fn reconnect(&mut self) -> Result<(), Error> { self.c.reconnect().await } } -impl Worker { +impl Worker { async fn new(c: Client, workers_count: usize, callbacks: CallbacksRegistry) -> Self { Worker { c, @@ -165,7 +165,7 @@ enum Failed { BadJobType(String), } -impl Worker { +impl Worker { async fn run_job(&mut self, job: Job) -> Result<(), Failed> { let handler = self .callbacks @@ -277,7 +277,7 @@ impl Worker { From 4b29f9c2a8700c4a88dd244a967d1106cbd10d36 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 18:53:15 +0500 Subject: [PATCH 087/129] Restore docs for WorkerBuilder::default --- src/worker/builder.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/worker/builder.rs b/src/worker/builder.rs index eba846b5..95fe0038 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -17,9 +17,16 @@ pub struct WorkerBuilder { } impl Default for WorkerBuilder { - /// Create a builder for asynchronous version of `Worker`. + /// Construct a new [`WorkerBuilder`](struct.WorkerBuilder.html) with default worker options and the url fetched from environment + /// variables. + /// + /// This will construct a worker where: + /// + /// - `hostname` is this machine's hostname. + /// - `wid` is a randomly generated string. + /// - `pid` is the OS PID of this process. + /// - `labels` is `["rust"]`. /// - /// See [`WorkerBuilder`](struct.WorkerBuilder.html) fn default() -> Self { WorkerBuilder { opts: ClientOptions::default(), From d869858dda6bc491a751505944515c478a000c44 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 18:58:34 +0500 Subject: [PATCH 088/129] Rm redunrant helper methods on Worker --- src/worker/mod.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/worker/mod.rs b/src/worker/mod.rs index be3a4cab..8d5a7ad3 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -1,6 +1,6 @@ use super::proto::{Client, Reconnect}; use crate::error::Error; -use crate::proto::{Ack, Fail, Job, JobId}; +use crate::proto::{Ack, Fail, Job}; use fnv::FnvHashMap; use std::sync::{atomic, Arc}; use std::{error::Error as StdError, sync::atomic::AtomicUsize}; @@ -174,14 +174,6 @@ impl handler.run(job).await.map_err(Failed::Application) } - async fn report_failure_to_server(&mut self, f: &Fail) -> Result<(), Error> { - self.c.issue(f).await?.read_ok().await - } - - async fn report_success_to_server(&mut self, jid: JobId) -> Result<(), Error> { - self.c.issue(&Ack::new(jid)).await?.read_ok().await - } - async fn report_on_all_workers(&mut self) -> Result<(), Error> { let worker_states = Arc::get_mut(&mut self.worker_states) .expect("all workers are scoped to &mut of the user-code-visible Worker"); @@ -258,7 +250,7 @@ impl match self.run_job(job).await { Ok(_) => { self.worker_states.register_success(worker, jid.clone()); - self.report_success_to_server(jid).await?; + self.c.issue(&Ack::new(jid)).await?.read_ok().await?; } Err(e) => { let fail = match e { @@ -266,7 +258,7 @@ impl Failed::Application(e) => Fail::generic_with_backtrace(jid, e), }; self.worker_states.register_failure(worker, fail.clone()); - self.report_failure_to_server(&fail).await?; + self.c.issue(&fail).await?.read_ok().await?; } } From 9329ea0afcee14cff5ba962a1033e4289c6fc26d Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 19:06:11 +0500 Subject: [PATCH 089/129] Restore docs on WorkerBuilder::connect --- src/worker/builder.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/worker/builder.rs b/src/worker/builder.rs index 95fe0038..58b23ad3 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -97,7 +97,7 @@ impl WorkerBuilder { self } - /// Asynchronously connect to a Faktory server with a non-standard stream. + /// Connect to a Faktory server with a non-standard stream. pub async fn connect_with( mut self, stream: S, @@ -110,9 +110,19 @@ impl WorkerBuilder { Ok(Worker::new(client, self.workers_count, self.callbacks).await) } - /// Asynchronously connect to a Faktory server. + /// Connect to a Faktory server. /// - /// See [`connect`](WorkerBuilder::connect). + /// If `url` is not given, will use the standard Faktory environment variables. Specifically, + /// `FAKTORY_PROVIDER` is read to get the name of the environment variable to get the address + /// from (defaults to `FAKTORY_URL`), and then that environment variable is read to get the + /// server address. If the latter environment variable is not defined, the connection will be + /// made to + /// + /// ```text + /// tcp://localhost:7419 + /// ``` + /// + /// If `url` is given, but does not specify a port, it defaults to 7419. pub async fn connect( mut self, url: Option<&str>, From 3b055857dbf617a86ad3ad3b0b67848a439f7d61 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 19:25:21 +0500 Subject: [PATCH 090/129] Place comments in worker::mod to original location --- src/worker/mod.rs | 14 ++++++++------ src/worker/state.rs | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/worker/mod.rs b/src/worker/mod.rs index 8d5a7ad3..060a26bc 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -212,19 +212,21 @@ impl Ok(()) } - // FAIL currently running jobs even though they're still running. - // Returns the number of workers that may still be processing jobs. - // We are ignoring any FAIL command issue errors, since this is already - // an "emergency" case. + /// Fail currently running jobs. + /// + /// This will FAIL _all_ the jobs even though they're still running. + /// Returns the number of workers that may still be processing jobs. async fn force_fail_all_workers(&mut self) -> usize { let mut running = 0; for wstate in &*self.worker_states { - let may_be_jid = wstate.lock().unwrap().take_cuurently_running(); + let may_be_jid = wstate.lock().unwrap().take_currently_running(); if let Some(jid) = may_be_jid { running += 1; - let f = Fail::new(jid, "unknown", "terminated"); + let f = Fail::generic(jid, "terminated"); let _ = match self.c.issue(&f).await { Ok(r) => r.read_ok().await, + // We are ignoring any FAIL command issue errors, since this is already + // an "emergency" case. Err(_) => continue, } .is_ok(); diff --git a/src/worker/state.rs b/src/worker/state.rs index 78f17d20..f8f8ea5d 100644 --- a/src/worker/state.rs +++ b/src/worker/state.rs @@ -15,7 +15,7 @@ impl WorkerState { self.last_job_result.take() } - pub(crate) fn take_cuurently_running(&mut self) -> Option { + pub(crate) fn take_currently_running(&mut self) -> Option { self.running_job.take() } From 14e06adcc805f23f79e5fdccd2c56eeb94e5a9b2 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 20:05:50 +0500 Subject: [PATCH 091/129] Rm redundate .take on statuses iterator --- src/worker/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker/mod.rs b/src/worker/mod.rs index 060a26bc..e4cffeaa 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -330,7 +330,7 @@ impl< .collect(); let mut workers = Vec::with_capacity(workers_count); - for (worker, status) in statuses.iter().enumerate().take(workers_count) { + for (worker, status) in statuses.iter().enumerate() { let handle = self .spawn_worker(Arc::clone(status), worker, queues) .await?; From df8a4b11720e00f053181cfb3418558b050d84fc Mon Sep 17 00:00:00 2001 From: --show-origin Date: Tue, 23 Apr 2024 20:22:05 +0500 Subject: [PATCH 092/129] Run cargo fmt --- src/worker/runner.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/worker/runner.rs b/src/worker/runner.rs index f3f12268..96b9e4bc 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -90,16 +90,16 @@ where } /// A wrapper for the userland's handler. -/// +/// /// The `Closure` newtype is introduced to avoid having to box a job handler: /// we can now use `Closure(handler)` instead of `Box::new(handler)` and make /// the compiler happy. -/// +/// /// The `repr(transparent)` macro is to guarantee that this single-field struct /// and the wrapped handler have the same layout and so it is safe to operate on /// the in-memory representations of _the_ handler (submitted to us /// from the userland) and its enclosed (by us) self. -/// +/// /// Ref: https://github.com/jonhoo/faktory-rs/pull/51 #[repr(transparent)] pub(crate) struct Closure(pub F); From 2bd215d37561344dbb882cccf5a34c4a8fe188b7 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 24 Apr 2024 19:06:55 +0500 Subject: [PATCH 093/129] Use JoinSet in Worker::run. Update consumer::terminate test. --- src/worker/mod.rs | 41 +++++++++---- tests/consumer.rs | 149 +++++++++++++++++++++++++++++++++++++--------- tests/mock/mod.rs | 4 ++ 3 files changed, 154 insertions(+), 40 deletions(-) diff --git a/src/worker/mod.rs b/src/worker/mod.rs index e4cffeaa..b7eaa86b 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -5,7 +5,7 @@ use fnv::FnvHashMap; use std::sync::{atomic, Arc}; use std::{error::Error as StdError, sync::atomic::AtomicUsize}; use tokio::io::{AsyncBufRead, AsyncWrite}; -use tokio::task::JoinHandle; +use tokio::task::{AbortHandle, JoinSet}; mod builder; mod health; @@ -277,6 +277,19 @@ impl< { async fn for_worker(&mut self) -> Result { Ok(Worker { + // We actually only need: + // + // 1) a connected client; + // 2) access to callback registry; + // 3) access to this worker's state (not all of them) + // + // For simplicity, we are currently creating a processing worker as a full replica + // of the coordinating worker. + // + // In the future though this can be updated to strip off `terminated` from + // the processing worker (as unused) and disallow access to other processing workers' + // states from inside this processing worker (as privilege not needed). + // c: self.c.connect_again().await?, callbacks: Arc::clone(&self.callbacks), worker_states: Arc::clone(&self.worker_states), @@ -284,18 +297,19 @@ impl< }) } - async fn spawn_worker( + async fn spawn_worker_into( &mut self, + set: &mut JoinSet>, status: Arc, worker: usize, queues: &[Q], - ) -> Result>, Error> + ) -> Result where Q: AsRef, { let mut w = self.for_worker().await?; let queues: Vec<_> = queues.iter().map(|s| s.as_ref().to_string()).collect(); - Ok(tokio::spawn(async move { + Ok(set.spawn(async move { while status.load(atomic::Ordering::SeqCst) == STATUS_RUNNING { if let Err(e) = w.run_one(worker, &queues[..]).await { status.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); @@ -319,7 +333,10 @@ impl< where Q: AsRef, { - assert!(!self.terminated, "do not re-run a terminated worker"); + assert!( + !self.terminated, + "do not re-run a terminated worker (coordinator)" + ); self.report_on_all_workers().await?; let workers_count = self.worker_states.len(); @@ -329,12 +346,11 @@ impl< .map(|_| Arc::new(atomic::AtomicUsize::new(STATUS_RUNNING))) .collect(); - let mut workers = Vec::with_capacity(workers_count); + let mut join_set = JoinSet::new(); for (worker, status) in statuses.iter().enumerate() { - let handle = self - .spawn_worker(Arc::clone(status), worker, queues) + let _abort_handle = self + .spawn_worker_into(&mut join_set, Arc::clone(status), worker, queues) .await?; - workers.push(handle) } let exit = self.listen_for_heartbeats(&statuses).await; @@ -342,7 +358,7 @@ impl< // there are a couple of cases here: // // - we got TERMINATE, so we should just return, even if a worker is still running - // - we got TERMINATE and all workers has exited + // - we got TERMINATE and all workers have exited // - we got an error from heartbeat() // self.terminated = exit.is_ok(); @@ -356,9 +372,10 @@ impl< // we want to expose worker errors, or otherwise the heartbeat error let mut results = Vec::with_capacity(workers_count); - for w in workers { - results.push(w.await.expect("joined ok")); + while let Some(res) = join_set.join_next().await { + results.push(res.expect("joined ok")); } + let result = results.into_iter().collect::, _>>(); match exit { diff --git a/tests/consumer.rs b/tests/consumer.rs index bf050ec4..7e274f2f 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -1,3 +1,59 @@ +/// This sketch should help appreciate how mock streams are being distributed across workers. +/// +/// Side-note. Note how `CLIENT` (another node), `WEB UI` (browser), `FAKTORY SERVER` (contribsys Faktory binary), +/// and `FAKTORY WORKER (COORDINATOR)` are all separate processes (but `client` and `worker` _can_ belong +/// to the same process), while processing workers are threads (tokio tasks) in the `FAKTORY WORKER` process. +/// +/// __________________________ +/// | | __________ +/// | CLIENT | | | +/// | (PRODUCING AND TRACKING) | | WEB UI | +/// |__________________________| |__________| +/// | | +/// |:7419 _________________ | +/// | | | |:7420 +/// |---------------> | FAKTORY SERVER | <-----| +/// | localhost:7419 | +/// | localhost:7420 | +/// |---------------> |_________________| +/// |:7419 +/// | +/// | ___________________________________________________________________________________________________ +/// | | | +/// |_____________ | FAKTORY WORKER (COORDINATOR) | +/// | | with at least N + 2 threads: main thread, heartbeat thread, and N processing worker threads - | +/// | | tokio tasks - the actual workers; the desired count is specified via WorkerBuilder::workers(N) | +/// | | | +/// | |--> HEARTBEAT | +/// | | - send b"BEAT {\"wid\":\"wid\"}" to Faktory every 5 seconds; | +/// | | - set workers quiet if Faktory asked so; | +/// | | - terminate workers if: | +/// | | - corresponding signal received from Faktory (returning Ok(true)) | +/// | | - one of the workers failed (returning Ok(false)) | +/// | | - critical error in HEARTBEART thread occurs (returning Err(e) | +/// | | | +/// | |--> WORKER (index 0) with the following life-cycle: | +/// | - get owned stream by reconnecting coordinator client via `self.stream.reconnect().await` | +/// | (which for TcpStream will lead to establishing a new TCP connection to localhost:7419); | | +/// | - init a `Client` (say HELLO to HI); | +/// | - loop { self.run_one().await } until critical error or signal from coordinator; | +/// | |--> ... | +/// | |--> WORKER (index N) | +/// |___________________________________________________________________________________________________| +/// +/// Note how each processing worker is getting its owned stream and how we can control which stream is return +/// by means of implementing `Reconnect` for the stream we are supplying to the `Worker` initially. +/// +/// So, what we are doing for testing purposes is: +/// 1) provide a [`Stream`] to [`connect_with`](`WorkerBuilder::connect_with`) that will be holding inside a vector of mock streams +/// and with a reference to the stream of current interest (see `mine` field on [`Stream`]) and a "pointer" (see `take_next` field +/// on the private `mock::inner::Innner`) to the stream that will be given away on next call of `reconnect`; +/// 2) implement [`AsyncRead`] and [`AsyncWrite`] for the [`Stream`] so that internally we are polling read and write against the stream +/// referenced by [`mine`](Stream::mine). +/// 3) implement [`faktory::Reconnect`] for the [`Stream`] in a way that each time they call the `reconnect` method of the stream +/// we set `mine` to reference the stream that the "pointer" is currently pointing to and increment the "pointer" by 1; +/// 4) implement `Drop` for `mock::Stream` in a way that if the value of the "pointer" is not equal the length of the internal +/// vector of streams, we panic to indicate that we mis-planned things when setting up the test; mod mock; use faktory::*; @@ -215,7 +271,7 @@ async fn well_behaved() { #[tokio::test(flavor = "multi_thread")] async fn no_first_job() { - let mut s = mock::Stream::new(2); + let mut s = mock::Stream::new(2); // main plus worker let mut c = WorkerBuilder::default(); c.wid(WorkerId::new("wid")); c.register("foobar", |_| async move { @@ -281,16 +337,16 @@ async fn no_first_job() { #[tokio::test(flavor = "multi_thread")] async fn well_behaved_many() { - let mut s = mock::Stream::new(3); - let mut c = WorkerBuilder::default(); - c.workers(2); - c.wid(WorkerId::new("wid")); - c.register("foobar", |_| async move { + let mut s = mock::Stream::new(3); // main plus 2 workers + let mut w = WorkerBuilder::default(); + w.workers(2); + w.wid(WorkerId::new("wid")); + w.register("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; Ok::<(), io::Error>(()) }); - let mut c = c.connect_with(s.clone(), None).await.unwrap(); + let mut w = w.connect_with(s.clone(), None).await.unwrap(); s.ignore(0); // push two jobs that'll take a while to run @@ -316,7 +372,7 @@ async fn well_behaved_many() { ); } - let jh = spawn(async move { c.run(&["default"]).await }); + let jh = spawn(async move { w.run(&["default"]).await }); // the running thread won't return for a while. the heartbeat thingy is going to eventually // send a heartbeat, and we want to respond to that with a "quiet" to make it not accept any @@ -357,17 +413,30 @@ async fn well_behaved_many() { #[tokio::test(flavor = "multi_thread")] async fn terminate() { - let mut s = mock::Stream::new(2); - let mut c: WorkerBuilder = WorkerBuilder::default(); - c.wid(WorkerId::new("wid")); - c.register("foobar", |_| async move { + // Internally, the `take_next` member on the `mock::Inner` struct will be incremented from `0` to `1`, + // while the `Stream::mine` wil be pointing to stream with index 0. See how we are later on ignoring bytes + // written to this stream by means of `s.ignore(0)`. + let mut s = mock::Stream::new(2); // main plus worker + + // prepare a worker with only never (!) returning handler + let mut w: WorkerBuilder = WorkerBuilder::default(); + w.hostname("machine".into()); + w.wid(WorkerId::new("wid")); + w.register("foobar", |_| async move { loop { sleep(Duration::from_secs(5)).await; } }); - let mut c = c.connect_with(s.clone(), None).await.unwrap(); + + let mut w = w.connect_with(s.clone(), None).await.unwrap(); + // what now is being ignored on `mine` channel are these written bytes (pid will vary): + // b"HELLO {\"hostname\":\"machine\",\"wid\":\"wid\",\"pid\":7332,\"labels\":[\"rust\"],\"v\":2}\r\n" + // this was the HELLO from main (coordinating) worker s.ignore(0); + // as if a producing client had sent this job to Faktory and Faktory, in its turn, + // had sent it to the processing (NB) worker, rather than coordinating one (note how we + // are passing `1` as first arg to `s.push_bytes_to_read`) s.push_bytes_to_read( 1, b"$186\r\n\ @@ -383,25 +452,34 @@ async fn terminate() { }\r\n", ); - let jh = spawn(async move { c.run(&["default"]).await }); + let jh = spawn(async move { + // Note how running a coordinating leads to mock::Stream::reconnect: + // `Worker::run` -> `Worker::spawn_worker_into` -> `Worker::for_worker` -> `Client::connect_again` -> `Stream::reconnect` + // + // So when the `w.run` is triggered, `Stream::reconnect` will fire and the `take_next` member on the `mock::Inner` struct + // will be incremented from `1` to `2`. But, most importently, `mine` will now be pointing to the second + // stream (stream with index 1) from this test, and the _actual_ worker (not the master worker (coordinator)) will + // be talking via this stream. + w.run(&["default"]).await + }); // the running thread won't ever return, because the job never exits. the heartbeat thingy is - // going to eventually send a heartbeat, and we want to respond to that with a "terminate" + // going to eventually (in ~5 seconds) send a heartbeat, and we want to respond to that with a "terminate" s.push_bytes_to_read(0, b"+{\"state\":\"terminate\"}\r\n"); // at this point, c.run() should immediately return with Ok(1) indicating that one job is still // running. assert_eq!(jh.await.unwrap().unwrap(), 1); - // heartbeat should have seen one beat (terminate) and then send FAIL + // Heartbeat Thread (stream with index 0). + // + // Heartbeat thread should have sent one BEAT command, then an immediate FAIL, and a final END: + // <---------- BEAT ---------><---------------------------- FAIL JOB -----------------------------------------><-END-> + // "BEAT {\"wid\":\"wid\"}\r\nFAIL {\"jid\":\"forever\",\"errtype\":\"unknown\",\"message\":\"terminated\"}\r\nEND\r\n" let written = s.pop_bytes_written(0); let beat = b"BEAT {\"wid\":\"wid\"}\r\nFAIL "; assert_eq!(&written[0..beat.len()], &beat[..]); assert!(written.ends_with(b"\r\nEND\r\n")); - println!( - "{}", - std::str::from_utf8(&written[beat.len()..(written.len() - b"\r\nEND\r\n".len())]).unwrap() - ); let written: serde_json::Value = serde_json::from_slice(&written[beat.len()..(written.len() - b"\r\nEND\r\n".len())]) .unwrap(); @@ -412,13 +490,28 @@ async fn terminate() { .and_then(|v| v.as_str()), Some("forever") ); - - // worker should have just fetched once + assert_eq!(written.get("errtype").unwrap().as_str(), Some("unknown")); + assert_eq!(written.get("message").unwrap().as_str(), Some("terminated")); + + // Let's give the worker's client a chance to complete clean up on Client's drop (effectively send `END\r\n`), + // and only after that pop bytes written into its stream. If we do not do this, we will end up with a flaky + // test, where `END\r\n` will sometimes make it to the writer and sometimes not. The `500` ms are empirical. + sleep(Duration::from_millis(500)).await; + + // Worker Thread (stream with index 1). + // + // Worker thread should have sent HELLO (which in coordinator case we thew away with `s.ignore(0)`), FETCH ( + // consume one job from the "default" queue), and END (which is performed as Client's clean-up). + // <------------------------------------ HELLO (PASSWORDLESS) -------------------------------------><--- FETCH -----><-END-> + // "HELLO {\"hostname\":\"machine\",\"wid\":\"wid\",\"pid\":12628,\"labels\":[\"rust\"],\"v\":2}\r\nFETCH default\r\nEND\r\n" let written = s.pop_bytes_written(1); - let msgs = "\r\n\ - FETCH default\r\n"; - assert_eq!( - std::str::from_utf8(&written[(written.len() - msgs.len())..]).unwrap(), - msgs - ); + assert!(written.starts_with(b"HELLO {\"hostname\":\"machine\",\"wid\":\"wid\"")); + assert!(written.ends_with(b"\r\nFETCH default\r\nEND\r\n")); + + // P.S. Interestingly, before we switched to `JoinSet` in `Worker::run` internals, this last `END\r\n` + // of the processing worker never actually got to the bytes written, no matter how much time you sleep + // before popping those bytes from the mock stream. + // + // But generally speaking, the graceful situation is when the number of `HI`s and the number of `END`s are + // equal. Why did they decide for `END` instead of `BYE` in Faktory ? :smile: } diff --git a/tests/mock/mod.rs b/tests/mock/mod.rs index 17c45327..f3858585 100644 --- a/tests/mock/mod.rs +++ b/tests/mock/mod.rs @@ -100,6 +100,10 @@ impl Stream { }; let mine = inner.take_stream().unwrap(); + // So if they asked for two stream (see `consumer::terminate` test), + // the first one will be `mine` while they both will be accessible + // internally via `all` (since `Inner::take_stream` is not actually + // taking, it is rather _cloning_). Stream { mine, all: Arc::new(Mutex::new(inner)), From 2d4da652651d0a15ef0f4355203b93ce4bfba192 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 24 Apr 2024 20:27:41 +0500 Subject: [PATCH 094/129] Update signature in batch ent methods to accept AsRef --- src/proto/batch/cmd.rs | 23 ++++++++++++++++------- src/proto/batch/handle.rs | 2 +- src/proto/batch/status.rs | 2 +- src/proto/client/ent.rs | 27 +++++++++++++++++---------- src/proto/single/id.rs | 6 ++++++ 5 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/proto/batch/cmd.rs b/src/proto/batch/cmd.rs index 27329a36..ecab54f2 100644 --- a/src/proto/batch/cmd.rs +++ b/src/proto/batch/cmd.rs @@ -14,30 +14,39 @@ impl FaktoryCommand for Batch { macro_rules! batch_cmd { ($structure:ident, $cmd:expr) => { - impl From for $structure { - fn from(value: BatchId) -> Self { + impl> From for $structure { + fn from(value: B) -> Self { $structure(value) } } #[async_trait::async_trait] - impl FaktoryCommand for $structure { + impl FaktoryCommand for $structure + where + B: AsRef + Sync, + { async fn issue(&self, w: &mut W) -> Result<(), Error> { w.write_all(b"BATCH ").await?; w.write_all($cmd.as_bytes()).await?; w.write_all(b" ").await?; - w.write_all(self.0.as_bytes()).await?; + w.write_all(self.0.as_ref().as_bytes()).await?; Ok(w.write_all(b"\r\n").await?) } } }; } -pub(crate) struct CommitBatch(BatchId); +pub(crate) struct CommitBatch(B) +where + B: AsRef; batch_cmd!(CommitBatch, "COMMIT"); -pub(crate) struct GetBatchStatus(BatchId); +pub(crate) struct GetBatchStatus(B) +where + B: AsRef; batch_cmd!(GetBatchStatus, "STATUS"); -pub(crate) struct OpenBatch(BatchId); +pub(crate) struct OpenBatch(B) +where + B: AsRef; batch_cmd!(OpenBatch, "OPEN"); diff --git a/src/proto/batch/handle.rs b/src/proto/batch/handle.rs index b8550a94..88dac29e 100644 --- a/src/proto/batch/handle.rs +++ b/src/proto/batch/handle.rs @@ -41,6 +41,6 @@ impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { /// Once committed, the batch can still be re-opened with [open_batch](Client::open_batch), /// and extra jobs can be added to it. pub async fn commit(self) -> Result<(), Error> { - self.c.commit_batch(self.bid).await + self.c.commit_batch(&self.bid).await } } diff --git a/src/proto/batch/status.rs b/src/proto/batch/status.rs index 9d52e38f..5a884f65 100644 --- a/src/proto/batch/status.rs +++ b/src/proto/batch/status.rs @@ -87,6 +87,6 @@ impl<'a> BatchStatus { &self, prod: &'a mut Client, ) -> Result>, Error> { - prod.open_batch(self.bid.clone()).await + prod.open_batch(&self.bid).await } } diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index c3c83daf..2f881c15 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -2,10 +2,10 @@ use super::super::batch::{CommitBatch, GetBatchStatus, OpenBatch}; use super::super::{single, BatchStatus, JobId, Progress, ProgressUpdate, Track}; use super::{Client, ReadToken}; use crate::ent::{Batch, BatchHandle, BatchId}; -use crate::error::Error; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use crate::error::{self, Error}; +use tokio::io::{AsyncBufRead, AsyncWrite}; -impl Client { +impl Client { /// Send information on a job's execution progress to Faktory. pub async fn set_progress(&mut self, upd: ProgressUpdate) -> Result<(), Error> { let cmd = Track::Set(upd); @@ -19,8 +19,11 @@ impl Client { } /// Fetch information on a batch of jobs execution progress. - pub async fn get_batch_status(&mut self, bid: BatchId) -> Result, Error> { - let cmd = GetBatchStatus::from(bid); + pub async fn get_batch_status(&mut self, bid: B) -> Result, Error> + where + B: AsRef + Sync, + { + let cmd = GetBatchStatus::from(&bid); self.issue(&cmd).await?.read_json().await } @@ -34,24 +37,28 @@ impl Client { /// /// This will not error if a batch with the provided `bid` does not exist, /// rather `Ok(None)` will be returned. - pub async fn open_batch(&mut self, bid: BatchId) -> Result>, Error> { + pub async fn open_batch(&mut self, bid: B) -> Result>, Error> + where + B: AsRef + Sync, + { let bid = self.issue(&OpenBatch::from(bid)).await?.maybe_bid().await?; Ok(bid.map(|bid| BatchHandle::new(bid, self))) } - pub(crate) async fn commit_batch(&mut self, bid: BatchId) -> Result<(), Error> { + pub(crate) async fn commit_batch(&mut self, bid: B) -> Result<(), Error> + where + B: AsRef + Sync, + { self.issue(&CommitBatch::from(bid)).await?.read_ok().await } } -impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> ReadToken<'a, S> { +impl<'a, S: AsyncBufRead + AsyncWrite + Unpin + Send> ReadToken<'a, S> { pub(crate) async fn read_bid(self) -> Result { single::read_bid(&mut self.0.stream).await } pub(crate) async fn maybe_bid(self) -> Result, Error> { - use crate::error; - let bid_read_res = single::read_bid(&mut self.0.stream).await; if bid_read_res.is_ok() { return Ok(Some(bid_read_res.unwrap())); diff --git a/src/proto/single/id.rs b/src/proto/single/id.rs index 9bb7b57c..cd07acc5 100644 --- a/src/proto/single/id.rs +++ b/src/proto/single/id.rs @@ -26,6 +26,12 @@ macro_rules! string_wrapper_impls { self.deref().as_ref() } } + + impl AsRef<$new_type> for $new_type { + fn as_ref(&self) -> &$new_type { + &self + } + } }; } From 07039830b76d76f3650578454222924781a3200a Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 24 Apr 2024 20:41:26 +0500 Subject: [PATCH 095/129] Add serde transparent for newtype ids --- src/proto/single/id.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/proto/single/id.rs b/src/proto/single/id.rs index cd07acc5..9add5eb4 100644 --- a/src/proto/single/id.rs +++ b/src/proto/single/id.rs @@ -43,6 +43,7 @@ macro_rules! string_wrapper_impls { /// If you do not have any domain, product or organisation specific requirements, you may prefer /// to have a random job identifier generated for you with [`random`](JobId::random). #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(transparent)] pub struct JobId(String); impl JobId { @@ -64,6 +65,7 @@ string_wrapper_impls!(JobId); /// If you do not have any domain, product or organisation specific requirements, you may prefer /// to have a random job identifier generated for you with [`random`](WorkerId::random). #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(transparent)] pub struct WorkerId(String); impl WorkerId { @@ -82,6 +84,7 @@ string_wrapper_impls!(WorkerId); /// This is a wrapper over the string identifier issued by the Faktory server. /// Only used for operations with [`Batch`](struct.Batch.html) in Enterprise Faktory. #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[serde(transparent)] pub struct BatchId(String); string_wrapper_impls!(BatchId); From 55d9beaac2f1d4ee51ef25676f5ed83009477db5 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 24 Apr 2024 21:31:05 +0500 Subject: [PATCH 096/129] Ask for String rather than &str in TlsStream::new --- src/tls/native_tls.rs | 15 +++++++-------- src/tls/rustls.rs | 5 ++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/tls/native_tls.rs b/src/tls/native_tls.rs index 95273a88..93df07c5 100644 --- a/src/tls/native_tls.rs +++ b/src/tls/native_tls.rs @@ -71,7 +71,7 @@ impl TlsStream { }?; let hostname = utils::host_from_url(&url); let tcp_stream = TokioTcpStream::connect(&hostname).await?; - Ok(TlsStream::new(tcp_stream, connector, &hostname).await?) + Ok(TlsStream::new(tcp_stream, connector, hostname).await?) } } @@ -84,7 +84,7 @@ where /// Internally creates a `ClientConfig` with an empty root certificates store and no client /// authentication. Use [`new`](TlsStream::new) for a customized `TlsConnector`. /// Create a new TLS connection on an existing stream. - pub async fn default(stream: S, hostname: &str) -> io::Result { + pub async fn default(stream: S, hostname: String) -> io::Result { let connector = TlsConnector::builder() .build() .map_err(error::TlsStream::Native) @@ -96,17 +96,16 @@ where pub async fn new( stream: S, connector: impl Into, - hostname: &str, + hostname: String, ) -> io::Result { - let domain = hostname.try_into().expect("a valid DNS name or IP address"); - let connector = connector.into(); + let connector: AsyncTlsConnector = connector.into(); let tls_stream = connector - .connect(domain, stream) + .connect(&hostname, stream) .await .map_err(|e| io::Error::new(io::ErrorKind::ConnectionAborted, e))?; Ok(TlsStream { connector, - hostname: hostname.into(), + hostname, stream: tls_stream, }) } @@ -125,7 +124,7 @@ where .get_mut() .reconnect() .await?; - Self::new(stream, self.connector.clone(), &self.hostname).await + Self::new(stream, self.connector.clone(), self.hostname.clone()).await } } diff --git a/src/tls/rustls.rs b/src/tls/rustls.rs index 24f7cba2..612e4930 100644 --- a/src/tls/rustls.rs +++ b/src/tls/rustls.rs @@ -82,7 +82,7 @@ impl TlsStream { }?; let host_and_port = utils::host_from_url(&url); let tcp_stream = TokioTcpStream::connect(&host_and_port).await?; - let host = url.host_str().unwrap(); + let host = url.host_str().unwrap().to_string().leak(); Ok(TlsStream::new(tcp_stream, connector, host).await?) } } @@ -107,9 +107,8 @@ where pub async fn new( stream: S, connector: TlsConnector, - hostname: impl Into, + hostname: &'static str, ) -> io::Result { - let hostname: &'static str = hostname.into().leak(); TlsStream::create_new(stream, connector, hostname).await } From 8d91c26e20724b2f966184bfe42f19d41e7981cf Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 24 Apr 2024 21:35:49 +0500 Subject: [PATCH 097/129] Rm redundant TlsStrean::create_new --- src/tls/rustls.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/tls/rustls.rs b/src/tls/rustls.rs index 612e4930..a08299a4 100644 --- a/src/tls/rustls.rs +++ b/src/tls/rustls.rs @@ -108,21 +108,6 @@ where stream: S, connector: TlsConnector, hostname: &'static str, - ) -> io::Result { - TlsStream::create_new(stream, connector, hostname).await - } - - /// Actually create new `TlsStream`. - /// - /// This private faktory method is needed to be able re-use the already `&'static str` hostname - /// when re-connecting, rather than allocate new String and leak it yet again. - /// - /// See how we are leaking the `hostname` in [`new`](TlsStream::new) constructor. This is needed - /// to satisfy the `tokio_rustls::TlsConnector::connect` which is expecting a `pki_types::ServerName<'static>`. - async fn create_new( - stream: S, - connector: TlsConnector, - hostname: &'static str, ) -> io::Result { let server_name = hostname.try_into().expect("a valid DNS name or IP address"); let tls_stream = connector @@ -144,7 +129,7 @@ where { async fn reconnect(&mut self) -> io::Result { let stream = self.stream.get_mut().0.reconnect().await?; - TlsStream::create_new(stream, self.connector.clone(), self.hostname).await + TlsStream::new(stream, self.connector.clone(), self.hostname).await } } From a44c63af8e5d1af41d081b19c09bd45d2edaddb9 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Wed, 24 Apr 2024 22:04:14 +0500 Subject: [PATCH 098/129] Pin serde at 1.0.186 to make min versions pass --- Cargo.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ea164c0e..6a4e8417 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,11 @@ oid-registry = "0.6.1" openssl = { version = "0.10.60", optional = true } rustls = "0.22.1" +# Lockstep between `serde` and `serde_derive` was introduced with the "pinned" release: +# https://github.com/serde-rs/serde/compare/v1.0.185...v1.0.186#diff-2843fc1320fa24a059f5ca967ee45d116110116263a8ba311a3aca3793c562f0R34-R41 +# Without this pin our `#[serde(transparent)]` and `#[derive(Serialize, Deserialize)] do not play well together. +serde = "1.0.186" + [[bin]] name = "loadtest" path = "src/bin/loadtest.rs" From a08854844daf7742f0f71114c2de13cd0746699c Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 00:42:21 +0500 Subject: [PATCH 099/129] Add WorkerBuilder::add_to_labels method. Demonstrate in test --- src/worker/builder.rs | 25 +++++++++++++++++++++++-- tests/consumer.rs | 11 +++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/worker/builder.rs b/src/worker/builder.rs index 58b23ad3..307c0470 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -56,8 +56,29 @@ impl WorkerBuilder { /// Set the labels to use for this worker. /// /// Defaults to `["rust"]`. - pub fn labels(&mut self, labels: Vec) -> &mut Self { - self.opts.labels = labels; + /// + /// Note that calling this overrides the labels set previously. + /// + /// If you need to extend the labels already set, use [`WorkerBuilder::add_to_labels`] instead. + pub fn labels(&mut self, labels: I) -> &mut Self + where + I: IntoIterator, + { + self.opts.labels = labels.into_iter().collect(); + self + } + + /// Extend the worker's labels. + /// + /// Note that calling this will add the provided labels to those that are already there or - + /// if no labels have been explicitly set before - to the default `"rust"` label. + /// + /// If you need to override the labels set previously, use [`WorkerBuilder::labels`] instead. + pub fn add_to_labels(&mut self, labels: I) -> &mut Self + where + I: IntoIterator, + { + self.opts.labels.extend(labels); self } diff --git a/tests/consumer.rs b/tests/consumer.rs index 7e274f2f..0810335e 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -66,7 +66,14 @@ async fn hello() { let mut c: WorkerBuilder = WorkerBuilder::default(); c.hostname("host".to_string()) .wid(WorkerId::new("wid")) - .labels(vec!["foo".to_string(), "bar".to_string()]); + .labels([ + "will".to_string(), + "be!".to_string(), + "overwritten".to_string(), + ]) + .labels(["foo".to_string(), "bar".to_string()]) + .add_to_labels(["will".to_string()]) + .add_to_labels(["be".to_string(), "added".to_string()]); c.register("never_called", |_j: Job| async move { unreachable!() }); let c = c.connect_with(s.clone(), None).await.unwrap(); let written = s.pop_bytes_written(0); @@ -81,7 +88,7 @@ async fn hello() { assert_eq!(written.get("pid").map(|h| h.is_number()), Some(true)); assert_eq!(written.get("v").and_then(|h| h.as_i64()), Some(2)); let labels = written["labels"].as_array().unwrap(); - assert_eq!(labels, &["foo", "bar"]); + assert_eq!(labels, &["foo", "bar", "will", "be", "added"]); drop(c); let written = s.pop_bytes_written(0); From ce4a65f820da91a4953ab8461056cac03e4fa78c Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 00:56:11 +0500 Subject: [PATCH 100/129] Rm empty line in WorkerBuilder::register --- src/worker/builder.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/worker/builder.rs b/src/worker/builder.rs index 307c0470..89e44363 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -97,7 +97,6 @@ impl WorkerBuilder { pub fn register(&mut self, kind: K, handler: H) -> &mut Self where K: Into, - H: Fn(Job) -> Fut + Send + Sync + 'static, Fut: Future> + Send, { From da28e8d85516a1ff67927fb92db0f10256a752be Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 07:05:37 +0500 Subject: [PATCH 101/129] Rename WorkerBuilder::register to WorkerBuilder::register_fn --- src/bin/loadtest.rs | 2 +- src/lib.rs | 2 +- src/worker/builder.rs | 6 +++--- src/worker/mod.rs | 6 +++--- src/worker/runner.rs | 2 +- tests/consumer.rs | 16 ++++++++-------- tests/real/community.rs | 20 ++++++++++---------- tests/real/enterprise.rs | 26 +++++++++++++------------- tests/tls/native_tls.rs | 2 +- tests/tls/rustls.rs | 2 +- 10 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index bdeccc17..aa1bec09 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -59,7 +59,7 @@ async fn main() { // make producer and consumer let mut p = Client::connect(None).await.unwrap(); let mut c = WorkerBuilder::default(); - c.register("SomeJob", |_| { + c.register_fn("SomeJob", |_| { Box::pin(async move { let mut rng = rand::thread_rng(); if rng.gen_bool(0.01) { diff --git a/src/lib.rs b/src/lib.rs index b73211f5..57a4a0be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,7 @@ //! use faktory::WorkerBuilder; //! use std::io; //! let mut w = WorkerBuilder::default(); -//! w.register("foobar", |job| async move { +//! w.register_fn("foobar", |job| async move { //! println!("{:?}", job); //! Ok::<(), io::Error>(()) //! }); diff --git a/src/worker/builder.rs b/src/worker/builder.rs index 89e44363..1cbdc2ef 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -94,13 +94,13 @@ impl WorkerBuilder { /// /// Whenever a job whose type matches `kind` is fetched from the Faktory, the given handler /// function is called with that job as its argument. - pub fn register(&mut self, kind: K, handler: H) -> &mut Self + pub fn register_fn(&mut self, kind: K, handler: H) -> &mut Self where K: Into, H: Fn(Job) -> Fut + Send + Sync + 'static, Fut: Future> + Send, { - self.register_runner(kind, Closure(handler)); + self.register(kind, Closure(handler)); self } @@ -108,7 +108,7 @@ impl WorkerBuilder { /// /// Whenever a job whose type matches `kind` is fetched from the Faktory, the given handler /// object is called with that job as its argument. - pub fn register_runner(&mut self, kind: K, runner: H) -> &mut Self + pub fn register(&mut self, kind: K, runner: H) -> &mut Self where K: Into, H: JobRunner + 'static, diff --git a/src/worker/mod.rs b/src/worker/mod.rs index b7eaa86b..2309d62e 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -112,7 +112,7 @@ type CallbacksRegistry = FnvHashMap>; /// /// let mut w = WorkerBuilder::default(); /// -/// w.register("foo", process_job); +/// w.register_fn("foo", process_job); /// /// let mut w = w.connect(None).await.unwrap(); /// if let Err(e) = w.run(&["default"]).await { @@ -127,14 +127,14 @@ type CallbacksRegistry = FnvHashMap>; /// # use faktory::WorkerBuilder; /// # use std::io; /// let mut w = WorkerBuilder::default(); -/// w.register("bar", |job| async move { +/// w.register_fn("bar", |job| async move { /// println!("{:?}", job); /// Ok::<(), io::Error>(()) /// }); /// ``` /// /// You can also register anything that implements [`JobRunner`] to handle jobs -/// with [`register_runner`](WorkerBuilder::register_runner). +/// with [`register`](WorkerBuilder::register). /// pub struct Worker { c: Client, diff --git a/src/worker/runner.rs b/src/worker/runner.rs index 96b9e4bc..ba2e1872 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -36,7 +36,7 @@ use std::future::Future; /// let handler = MyHandler { /// config: "bar".to_string(), /// }; -/// w.register_runner("foo", handler); +/// w.register("foo", handler); /// let mut w = w.connect(None).await.unwrap(); /// if let Err(e) = w.run(&["default"]).await { /// println!("worker failed: {}", e); diff --git a/tests/consumer.rs b/tests/consumer.rs index 0810335e..b6ef494d 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -74,7 +74,7 @@ async fn hello() { .labels(["foo".to_string(), "bar".to_string()]) .add_to_labels(["will".to_string()]) .add_to_labels(["be".to_string(), "added".to_string()]); - c.register("never_called", |_j: Job| async move { unreachable!() }); + c.register_fn("never_called", |_j: Job| async move { unreachable!() }); let c = c.connect_with(s.clone(), None).await.unwrap(); let written = s.pop_bytes_written(0); assert!(written.starts_with(b"HELLO {")); @@ -100,7 +100,7 @@ async fn hello_pwd() { let mut s = mock::Stream::with_salt(1545, "55104dc76695721d"); let mut c: WorkerBuilder = WorkerBuilder::default(); - c.register("never_called", |_j: Job| async move { unreachable!() }); + c.register_fn("never_called", |_j: Job| async move { unreachable!() }); let c = c .connect_with(s.clone(), Some("foobar".to_string())) .await @@ -121,7 +121,7 @@ async fn hello_pwd() { async fn dequeue() { let mut s = mock::Stream::default(); let mut c = WorkerBuilder::default(); - c.register("foobar", |job: Job| async move { + c.register_fn("foobar", |job: Job| async move { assert_eq!(job.args(), &["z"]); Ok::<(), io::Error>(()) }); @@ -160,7 +160,7 @@ async fn dequeue() { async fn dequeue_first_empty() { let mut s = mock::Stream::default(); let mut c = WorkerBuilder::default(); - c.register("foobar", |job: Job| async move { + c.register_fn("foobar", |job: Job| async move { assert_eq!(job.args(), &["z"]); Ok::<(), io::Error>(()) }); @@ -216,7 +216,7 @@ async fn well_behaved() { let mut s = mock::Stream::new(2); // main plus worker let mut c = WorkerBuilder::default(); c.wid(WorkerId::new("wid")); - c.register("foobar", |_| async move { + c.register_fn("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; Ok::<(), io::Error>(()) @@ -281,7 +281,7 @@ async fn no_first_job() { let mut s = mock::Stream::new(2); // main plus worker let mut c = WorkerBuilder::default(); c.wid(WorkerId::new("wid")); - c.register("foobar", |_| async move { + c.register_fn("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; Ok::<(), io::Error>(()) @@ -348,7 +348,7 @@ async fn well_behaved_many() { let mut w = WorkerBuilder::default(); w.workers(2); w.wid(WorkerId::new("wid")); - w.register("foobar", |_| async move { + w.register_fn("foobar", |_| async move { // NOTE: this time needs to be so that it lands between the first heartbeat and the second sleep(Duration::from_secs(7)).await; Ok::<(), io::Error>(()) @@ -429,7 +429,7 @@ async fn terminate() { let mut w: WorkerBuilder = WorkerBuilder::default(); w.hostname("machine".into()); w.wid(WorkerId::new("wid")); - w.register("foobar", |_| async move { + w.register_fn("foobar", |_| async move { loop { sleep(Duration::from_secs(5)).await; } diff --git a/tests/real/community.rs b/tests/real/community.rs index f9bdb208..b2157b07 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -16,7 +16,7 @@ async fn hello_worker() { let mut c = WorkerBuilder::::default(); c.hostname("tester".to_string()) .labels(vec!["foo".to_string(), "bar".to_string()]); - c.register("never_called", |_| async move { unreachable!() }); + c.register_fn("never_called", |_| async move { unreachable!() }); let c = c.connect(None).await.unwrap(); drop(c); } @@ -40,13 +40,13 @@ async fn roundtrip() { .labels(vec!["rust".into(), local.into()]) .workers(1) .wid(WorkerId::random()) - .register("order", move |job| async move { + .register_fn("order", move |job| async move { assert_eq!(job.kind(), "order"); assert_eq!(job.queue, local); assert_eq!(job.args(), &[Value::from("ISBN-13:9781718501850")]); Ok::<(), io::Error>(()) }) - .register("image", |_| async move { unreachable!() }); + .register_fn("image", |_| async move { unreachable!() }); let mut worker = worker.connect(None).await.unwrap(); let mut client = Client::connect(None).await.unwrap(); client @@ -76,7 +76,7 @@ async fn multi() { let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(WorkerId::new(local)); - c.register(local, move |j| { + c.register_fn(local, move |j| { let tx = sync::Arc::clone(&tx); Box::pin(async move { tx.lock().unwrap().send(j).unwrap(); @@ -117,7 +117,7 @@ async fn fail() { let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(WorkerId::new(local)); - c.register(local, move |j| { + c.register_fn(local, move |j| { let tx = sync::Arc::clone(&tx); Box::pin(async move { tx.lock().unwrap().send(j).unwrap(); @@ -153,7 +153,7 @@ async fn queue() { let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(WorkerId::new(local)); - c.register(local, move |_job| { + c.register_fn(local, move |_job| { let tx = sync::Arc::clone(&tx); Box::pin(async move { tx.lock().unwrap().send(true) }) }); @@ -250,10 +250,10 @@ async fn test_jobs_pushed_in_bulk() { // is not an all-or-nothing operation: let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(WorkerId::new(local_3)); - c.register("very_special", move |_job| async { + c.register_fn("very_special", move |_job| async { Ok::<(), io::Error>(()) }); - c.register("broken", move |_job| async { Ok::<(), io::Error>(()) }); + c.register_fn("broken", move |_job| async { Ok::<(), io::Error>(()) }); let mut c = c.connect(None).await.unwrap(); // we targeted "very_special" jobs to "local_4" queue @@ -283,8 +283,8 @@ async fn test_jobs_created_with_builder() { // prepare a client and a worker: let mut cl = Client::connect(None).await.unwrap(); let mut w = WorkerBuilder::default(); - w.register("rebuild_index", assert_args_empty); - w.register("register_order", assert_args_not_empty); + w.register_fn("rebuild_index", assert_args_empty); + w.register_fn("register_order", assert_args_not_empty); let mut w = w.connect(None).await.unwrap(); diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index cb81d69d..db6dda42 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -43,7 +43,7 @@ async fn ent_expiring_job() { // prepare a client and a worker: let mut p = Client::connect(Some(&url)).await.unwrap(); let mut c = WorkerBuilder::default(); - c.register("AnExpiringJob", print_job); + c.register_fn("AnExpiringJob", print_job); let mut c = c.connect(Some(&url)).await.unwrap(); // prepare an expiring job: @@ -92,7 +92,7 @@ async fn ent_unique_job() { // prepare client and worker: let mut p = Client::connect(Some(&url)).await.unwrap(); let mut c = WorkerBuilder::default(); - c.register(job_type, print_job); + c.register_fn(job_type, print_job); let mut c = c.connect(Some(&url)).await.unwrap(); // Reminder. Jobs are considered unique for kind + args + queue. @@ -208,7 +208,7 @@ async fn ent_unique_job_until_success() { // to work hard: let mut client_a = Client::connect(Some(&url1)).await.unwrap(); let mut worker_a = WorkerBuilder::default(); - worker_a.register(job_type, |job| async move { + worker_a.register_fn(job_type, |job| async move { let args = job.args().to_owned(); let mut args = args.iter(); let diffuculty_level = args @@ -288,7 +288,7 @@ async fn ent_unique_job_until_start() { let handle = tokio::spawn(async move { let mut client_a = Client::connect(Some(&url1)).await.unwrap(); let mut worker_a = WorkerBuilder::default(); - worker_a.register(job_type, |job| async move { + worker_a.register_fn(job_type, |job| async move { let args = job.args().to_owned(); let mut args = args.iter(); let diffuculty_level = args @@ -378,7 +378,7 @@ async fn ent_unique_job_bypass_unique_lock() { // let's consume three times from the queue to verify that the first two jobs // have been enqueued for real, while the last one has not. let mut c = WorkerBuilder::default(); - c.register("order", print_job); + c.register_fn("order", print_job); let mut c = c.connect(Some(&url)).await.unwrap(); assert!(c.run_one(0, &[queue_name]).await.unwrap()); @@ -427,7 +427,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { { let job_id = job_id.clone(); let url = url.clone(); - c.register("order", move |job| { + c.register_fn("order", move |job| { let job_id = job_id.clone(); let url = url.clone(); Box::pin(async move { @@ -558,8 +558,8 @@ async fn test_batch_of_jobs_can_be_initiated() { let mut p = Client::connect(Some(&url)).await.unwrap(); let mut c = WorkerBuilder::default(); - c.register("thumbnail", |_job| async { Ok::<(), io::Error>(()) }); - c.register("clean_up", |_job| async { Ok(()) }); + c.register_fn("thumbnail", |_job| async { Ok::<(), io::Error>(()) }); + c.register_fn("clean_up", |_job| async { Ok(()) }); let mut c = c.connect(Some(&url)).await.unwrap(); let mut t = Client::connect(Some(&url)) .await @@ -695,7 +695,7 @@ async fn test_batches_can_be_nested() { // Set up 'client', 'worker', and 'tracker': let mut p = Client::connect(Some(&url)).await.unwrap(); let mut c = WorkerBuilder::default(); - c.register("jobtype", |_job| async { Ok::<(), io::Error>(()) }); + c.register_fn("jobtype", |_job| async { Ok::<(), io::Error>(()) }); let mut _c = c.connect(Some(&url)).await.unwrap(); let mut t = Client::connect(Some(&url)) .await @@ -796,8 +796,8 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { let mut cl = Client::connect(Some(&url)).await.unwrap(); let mut tr = Client::connect(Some(&url)).await.unwrap(); let mut w = WorkerBuilder::default(); - w.register("order", |_job| async { Ok(()) }); - w.register("order_clean_up", |_job| async { Ok::<(), io::Error>(()) }); + w.register_fn("order", |_job| async { Ok(()) }); + w.register_fn("order_clean_up", |_job| async { Ok::<(), io::Error>(()) }); let mut c = w.connect(Some(&url)).await.unwrap(); let mut jobs = some_jobs( @@ -928,8 +928,8 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { assert_eq!(s.success_callback_state, CallbackState::Pending); let mut c = WorkerBuilder::default(); - c.register(complete_cb_jobtype, |_job| async { Ok(()) }); - c.register(success_cb_jobtype, |_job| async { + c.register_fn(complete_cb_jobtype, |_job| async { Ok(()) }); + c.register_fn(success_cb_jobtype, |_job| async { Err(io::Error::new( io::ErrorKind::Other, "we want this one to fail to test the 'CallbackState' behavior", diff --git a/tests/tls/native_tls.rs b/tests/tls/native_tls.rs index 19263fc5..b159c959 100644 --- a/tests/tls/native_tls.rs +++ b/tests/tls/native_tls.rs @@ -26,7 +26,7 @@ async fn roundtrip_tls() { let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(WorkerId::new(local)); - c.register_runner(local, fixtures::JobHandler::new(tx)); + c.register(local, fixtures::JobHandler::new(tx)); let tls = || async { let connector = TlsConnector::builder() diff --git a/tests/tls/rustls.rs b/tests/tls/rustls.rs index 8885d353..a7fa428b 100644 --- a/tests/tls/rustls.rs +++ b/tests/tls/rustls.rs @@ -28,7 +28,7 @@ async fn roundtrip_tls() { let mut c = WorkerBuilder::default(); c.hostname("tester".to_string()).wid(WorkerId::new(local)); - c.register_runner(local, fixtures::JobHandler::new(tx)); + c.register(local, fixtures::JobHandler::new(tx)); let tls = || async { let verifier = fixtures::TestServerCertVerifier::new( From 050a28d0422329d4b0db5b4211b1d6ba088e26a9 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 07:39:29 +0500 Subject: [PATCH 102/129] Add to WorkerBuilder::register and ::register_fn docs --- src/worker/builder.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/worker/builder.rs b/src/worker/builder.rs index 1cbdc2ef..e737e557 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -94,6 +94,9 @@ impl WorkerBuilder { /// /// Whenever a job whose type matches `kind` is fetched from the Faktory, the given handler /// function is called with that job as its argument. + /// + /// Note that only one single handler per job kind is supported. Registering another handler + /// for the same job kind will silently override the handler registered previously. pub fn register_fn(&mut self, kind: K, handler: H) -> &mut Self where K: Into, @@ -108,6 +111,9 @@ impl WorkerBuilder { /// /// Whenever a job whose type matches `kind` is fetched from the Faktory, the given handler /// object is called with that job as its argument. + /// + /// Note that only one single handler per job kind is supported. Registering another handler + /// for the same job kind will silently override the handler registered previously. pub fn register(&mut self, kind: K, runner: H) -> &mut Self where K: Into, From 6644a2f46f08a9a4de23ab3d5bc736558f7dfdc1 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 07:44:30 +0500 Subject: [PATCH 103/129] Re-use WorkerBuilder::connect_with in ::connect --- src/worker/builder.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/worker/builder.rs b/src/worker/builder.rs index e737e557..8bd15d03 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -150,14 +150,11 @@ impl WorkerBuilder { /// /// If `url` is given, but does not specify a port, it defaults to 7419. pub async fn connect( - mut self, + self, url: Option<&str>, ) -> Result, E>, Error> { let url = utils::parse_provided_or_from_env(url)?; let stream = TokioStream::connect(utils::host_from_url(&url)).await?; - self.opts.is_worker = true; - let buffered = BufStream::new(stream); - let client = Client::new(buffered, self.opts).await?; - Ok(Worker::new(client, self.workers_count, self.callbacks).await) + self.connect_with(stream, None).await } } From 6e5121a0c745fd6c2f64b01de847005129ca8992 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 08:18:27 +0500 Subject: [PATCH 104/129] Do not use *Ext as bounds --- src/proto/batch/cmd.rs | 6 +++--- src/proto/batch/handle.rs | 6 +++--- src/proto/batch/status.rs | 4 ++-- src/proto/client/mod.rs | 17 ++++++++--------- src/proto/single/cmd.rs | 20 ++++++++++---------- src/proto/single/ent/cmd.rs | 4 ++-- src/proto/single/mod.rs | 8 +++++--- src/proto/single/resp.rs | 17 +++++++++-------- src/worker/health.rs | 4 ++-- 9 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/proto/batch/cmd.rs b/src/proto/batch/cmd.rs index ecab54f2..1c1df52f 100644 --- a/src/proto/batch/cmd.rs +++ b/src/proto/batch/cmd.rs @@ -1,10 +1,10 @@ use crate::error::Error; use crate::proto::{single::FaktoryCommand, Batch, BatchId}; -use tokio::io::AsyncWriteExt; +use tokio::io::{AsyncWrite, AsyncWriteExt}; #[async_trait::async_trait] impl FaktoryCommand for Batch { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { w.write_all(b"BATCH NEW ").await?; let r = serde_json::to_vec(self).map_err(Error::Serialization)?; w.write_all(&r).await?; @@ -25,7 +25,7 @@ macro_rules! batch_cmd { where B: AsRef + Sync, { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { w.write_all(b"BATCH ").await?; w.write_all($cmd.as_bytes()).await?; w.write_all(b" ").await?; diff --git a/src/proto/batch/handle.rs b/src/proto/batch/handle.rs index 88dac29e..9b7f6238 100644 --- a/src/proto/batch/handle.rs +++ b/src/proto/batch/handle.rs @@ -1,14 +1,14 @@ use crate::error::Error; use crate::proto::{Batch, BatchId, Client, Job}; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::io::{AsyncBufRead, AsyncWrite}; /// Represents a newly started or re-opened batch of jobs. -pub struct BatchHandle<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> { +pub struct BatchHandle<'a, S: AsyncBufRead + AsyncWrite + Unpin + Send> { bid: BatchId, c: &'a mut Client, } -impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> BatchHandle<'a, S> { +impl<'a, S: AsyncBufRead + AsyncWrite + Unpin + Send> BatchHandle<'a, S> { /// ID issued by the Faktory server to this batch. pub fn id(&self) -> &BatchId { &self.bid diff --git a/src/proto/batch/status.rs b/src/proto/batch/status.rs index 5a884f65..b1c66332 100644 --- a/src/proto/batch/status.rs +++ b/src/proto/batch/status.rs @@ -5,7 +5,7 @@ use super::BatchHandle; use crate::error::Error; use crate::proto::{BatchId, Client}; use chrono::{DateTime, Utc}; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::io::{AsyncBufRead, AsyncWrite}; // Not documented, but existing de fakto and also mentioned in the official client // https://github.com/contribsys/faktory/blob/main/client/batch.go#L17-L19 @@ -83,7 +83,7 @@ impl<'a> BatchStatus { /// Open the batch for which this `BatchStatus` has been retrieved. /// /// See [`open_batch`](Client::open_batch). - pub async fn open( + pub async fn open( &self, prod: &'a mut Client, ) -> Result>, Error> { diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index f2d3ade6..bdcc6af7 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -10,8 +10,7 @@ use super::{utils, PushBulk}; use crate::error::{self, Error}; use crate::{Job, WorkerId}; use std::collections::HashMap; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; -use tokio::io::{AsyncRead, AsyncWrite, BufStream}; +use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite, BufStream}; use tokio::net::TcpStream as TokioStream; mod options; @@ -146,14 +145,14 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// # Ok::<(), faktory::Error>(()) /// }); /// ``` -pub struct Client { +pub struct Client { stream: S, opts: ClientOptions, } impl Client where - S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send + Reconnect, + S: AsyncBufRead + AsyncWrite + Unpin + Send + Reconnect, { pub(crate) async fn connect_again(&mut self) -> Result { let s = self.stream.reconnect().await?; @@ -168,7 +167,7 @@ where impl Drop for Client where - S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, + S: AsyncBufRead + AsyncWrite + Unpin + Send, { fn drop(&mut self) { tokio::task::block_in_place(|| { @@ -223,7 +222,7 @@ impl Client> { impl Client where - S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, + S: AsyncBufRead + AsyncWrite + Unpin + Send, { async fn init(&mut self) -> Result<(), Error> { let hi = single::read_hi(&mut self.stream).await?; @@ -317,7 +316,7 @@ where impl Client where - S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send, + S: AsyncBufRead + AsyncWrite + Unpin + Send, { /// Enqueue the given job on the Faktory server. /// @@ -395,9 +394,9 @@ where pub struct ReadToken<'a, S>(pub(crate) &'a mut Client) where - S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send; + S: AsyncBufRead + AsyncWrite + Unpin + Send; -impl<'a, S: AsyncBufReadExt + AsyncWriteExt + Unpin + Send> ReadToken<'a, S> { +impl<'a, S: AsyncBufRead + AsyncWrite + Unpin + Send> ReadToken<'a, S> { pub(crate) async fn read_ok(self) -> Result<(), Error> { single::read_ok(&mut self.0.stream).await } diff --git a/src/proto/single/cmd.rs b/src/proto/single/cmd.rs index cc1b7917..54fb7115 100644 --- a/src/proto/single/cmd.rs +++ b/src/proto/single/cmd.rs @@ -1,18 +1,18 @@ use crate::error::Error; use crate::proto::{Job, JobId, WorkerId}; use std::error::Error as StdError; -use tokio::io::AsyncWriteExt; +use tokio::io::{AsyncWrite, AsyncWriteExt}; #[async_trait::async_trait] pub trait FaktoryCommand { - async fn issue(&self, w: &mut W) -> Result<(), Error>; + async fn issue(&self, w: &mut W) -> Result<(), Error>; } macro_rules! self_to_cmd { ($struct:ident, $cmd:expr) => { #[async_trait::async_trait] impl FaktoryCommand for $struct { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { w.write_all($cmd.as_bytes()).await?; w.write_all(b" ").await?; let r = serde_json::to_vec(self).map_err(Error::Serialization)?; @@ -27,7 +27,7 @@ macro_rules! self_to_cmd { /// followed by space separated queue names. async fn write_queues(w: &mut W, queues: &[S]) -> Result<(), Error> where - W: AsyncWriteExt + Unpin + Send, + W: AsyncWrite + Unpin + Send, S: AsRef, { for q in queues { @@ -44,7 +44,7 @@ pub(crate) struct Info; #[async_trait::async_trait] impl FaktoryCommand for Info { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { Ok(w.write_all(b"INFO\r\n").await?) } } @@ -140,7 +140,7 @@ pub(crate) struct End; #[async_trait::async_trait] impl FaktoryCommand for End { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { Ok(w.write_all(b"END\r\n").await?) } } @@ -159,7 +159,7 @@ impl<'a, Q> FaktoryCommand for Fetch<'a, Q> where Q: AsRef + Sync, { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { w.write_all(b"FETCH").await?; write_queues(w, self.queues).await?; Ok(w.write_all(b"\r\n").await?) @@ -246,7 +246,7 @@ impl From for Push { #[async_trait::async_trait] impl FaktoryCommand for Push { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { w.write_all(b"PUSH ").await?; let r = serde_json::to_vec(&**self).map_err(Error::Serialization)?; w.write_all(&r).await?; @@ -266,7 +266,7 @@ impl From> for PushBulk { #[async_trait::async_trait] impl FaktoryCommand for PushBulk { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { w.write_all(b"PUSHB ").await?; let r = serde_json::to_vec(&self.0).map_err(Error::Serialization)?; w.write_all(&r).await?; @@ -294,7 +294,7 @@ impl FaktoryCommand for QueueControl<'_, Q> where Q: AsRef + Sync, { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { let command = match self.action { QueueAction::Pause => b"QUEUE PAUSE".as_ref(), QueueAction::Resume => b"QUEUE RESUME".as_ref(), diff --git a/src/proto/single/ent/cmd.rs b/src/proto/single/ent/cmd.rs index acb38c8f..43881e44 100644 --- a/src/proto/single/ent/cmd.rs +++ b/src/proto/single/ent/cmd.rs @@ -1,7 +1,7 @@ use super::ProgressUpdate; use crate::error::Error; use crate::proto::{single::FaktoryCommand, JobId}; -use tokio::io::AsyncWriteExt; +use tokio::io::{AsyncWrite, AsyncWriteExt}; #[derive(Debug, Clone)] pub enum Track { @@ -11,7 +11,7 @@ pub enum Track { #[async_trait::async_trait] impl FaktoryCommand for Track { - async fn issue(&self, w: &mut W) -> Result<(), Error> { + async fn issue(&self, w: &mut W) -> Result<(), Error> { match self { Self::Set(upd) => { w.write_all(b"TRACK SET ").await?; diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index fce78470..a563e7ac 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -2,7 +2,7 @@ use crate::Error; use chrono::{DateTime, Utc}; use derive_builder::Builder; use std::collections::HashMap; -use tokio::io::{AsyncBufRead, AsyncWriteExt}; +use tokio::io::{AsyncBufRead, AsyncWrite}; mod cmd; mod id; @@ -260,7 +260,9 @@ impl Job { &self.failure } } -pub async fn write_command( + +use tokio::io::AsyncWriteExt; +pub async fn write_command( w: &mut W, command: &C, ) -> Result<(), Error> { @@ -269,7 +271,7 @@ pub async fn write_command( } pub async fn write_command_and_await_ok< - S: AsyncBufRead + AsyncWriteExt + Unpin + Send, + S: AsyncBufRead + AsyncWrite + Unpin + Send, C: FaktoryCommand, >( stream: &mut S, diff --git a/src/proto/single/resp.rs b/src/proto/single/resp.rs index bd0a07b9..5d4ede59 100644 --- a/src/proto/single/resp.rs +++ b/src/proto/single/resp.rs @@ -2,7 +2,7 @@ use crate::ent::BatchId; use crate::error::{self, Error}; -use tokio::io::{AsyncBufReadExt, AsyncReadExt}; +use tokio::io::AsyncBufRead; pub fn bad(expected: &'static str, got: &RawResponse) -> error::Protocol { let stringy = match *got { @@ -31,7 +31,7 @@ pub fn bad(expected: &'static str, got: &RawResponse) -> error::Protocol { // ---------------------------------------------- -pub async fn read_json( +pub async fn read_json( r: R, ) -> Result, Error> { let rr = read(r).await?; @@ -65,7 +65,7 @@ pub async fn read_json(r: R) -> Result { +pub async fn read_bid(r: R) -> Result { match read(r).await? { RawResponse::Blob(ref b) if b.is_empty() => Err(error::Protocol::BadType { expected: "non-empty blob representation of batch id", @@ -95,7 +95,7 @@ pub struct Hi { pub salt: Option, } -pub async fn read_hi(r: R) -> Result { +pub async fn read_hi(r: R) -> Result { let rr = read(r).await?; if let RawResponse::String(ref s) = rr { if let Some(s) = s.strip_prefix("HI ") { @@ -107,7 +107,7 @@ pub async fn read_hi(r: R) -> Result { // ---------------------------------------------- -pub async fn read_ok(r: R) -> Result<(), Error> { +pub async fn read_ok(r: R) -> Result<(), Error> { let rr = read(r).await?; if let RawResponse::String(ref s) = rr { if s == "OK" { @@ -132,9 +132,10 @@ pub enum RawResponse { Null, } +use tokio::io::{AsyncBufReadExt, AsyncReadExt}; async fn read(mut r: R) -> Result where - R: AsyncReadExt + AsyncBufReadExt + Unpin, + R: AsyncBufRead + Unpin, { let mut cmdbuf = [0u8; 1]; r.read_exact(&mut cmdbuf).await?; @@ -260,9 +261,9 @@ mod test { use crate::error::{self, Error}; use serde_json::{Map, Value}; use std::io::Cursor; - use tokio::io::AsyncBufReadExt; + use tokio::io::AsyncBufRead; - async fn read_json(c: C) -> Result, Error> { + async fn read_json(c: C) -> Result, Error> { super::read_json(c).await } diff --git a/src/worker/health.rs b/src/worker/health.rs index a9f98da1..3ffcd0b0 100644 --- a/src/worker/health.rs +++ b/src/worker/health.rs @@ -5,11 +5,11 @@ use std::{ sync::{atomic, Arc}, time, }; -use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; +use tokio::io::{AsyncBufRead, AsyncWrite}; use tokio::time::sleep as tokio_sleep; impl< - S: AsyncBufReadExt + AsyncWriteExt + Reconnect + Send + Unpin + 'static, + S: AsyncBufRead + AsyncWrite + Reconnect + Send + Unpin + 'static, E: StdError + 'static + Send, > Worker { From 04906376bfbee892a695c2d4e9c9e839e80ff685 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 08:22:13 +0500 Subject: [PATCH 105/129] Clean up worker::health module --- src/worker/health.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/worker/health.rs b/src/worker/health.rs index 3ffcd0b0..8e6f88da 100644 --- a/src/worker/health.rs +++ b/src/worker/health.rs @@ -8,14 +8,14 @@ use std::{ use tokio::io::{AsyncBufRead, AsyncWrite}; use tokio::time::sleep as tokio_sleep; -impl< - S: AsyncBufRead + AsyncWrite + Reconnect + Send + Unpin + 'static, - E: StdError + 'static + Send, - > Worker +impl Worker +where + S: AsyncBufRead + AsyncWrite + Reconnect + Send + Unpin + 'static, + E: StdError + 'static + Send, { pub(crate) async fn listen_for_heartbeats( &mut self, - statuses: &Vec>, + statuses: &[Arc], ) -> Result { let mut target = STATUS_RUNNING; From dfda0dbf7aa97a9cf2b805b3016702d7e136c32f Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 09:09:02 +0500 Subject: [PATCH 106/129] Rm excessive bounds --- src/proto/batch/handle.rs | 16 ++++++++++------ src/proto/client/mod.rs | 4 ++-- src/worker/mod.rs | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/proto/batch/handle.rs b/src/proto/batch/handle.rs index 9b7f6238..5015510d 100644 --- a/src/proto/batch/handle.rs +++ b/src/proto/batch/handle.rs @@ -3,21 +3,25 @@ use crate::proto::{Batch, BatchId, Client, Job}; use tokio::io::{AsyncBufRead, AsyncWrite}; /// Represents a newly started or re-opened batch of jobs. -pub struct BatchHandle<'a, S: AsyncBufRead + AsyncWrite + Unpin + Send> { +pub struct BatchHandle<'a, S: AsyncWrite + Unpin + Send> { bid: BatchId, c: &'a mut Client, } -impl<'a, S: AsyncBufRead + AsyncWrite + Unpin + Send> BatchHandle<'a, S> { +impl<'a, S: AsyncWrite + Unpin + Send> BatchHandle<'a, S> { + pub(crate) fn new(bid: BatchId, c: &mut Client) -> BatchHandle<'_, S> { + BatchHandle { bid, c } + } +} + +impl<'a, S: AsyncWrite + Unpin + Send> BatchHandle<'a, S> { /// ID issued by the Faktory server to this batch. pub fn id(&self) -> &BatchId { &self.bid } +} - pub(crate) fn new(bid: BatchId, c: &mut Client) -> BatchHandle<'_, S> { - BatchHandle { bid, c } - } - +impl<'a, S: AsyncBufRead + AsyncWrite + Unpin + Send> BatchHandle<'a, S> { /// Add the given job to the batch. /// /// Should the submitted job - for whatever reason - already have a `bid` key present in its custom hash, diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index bdcc6af7..9ba6477e 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -145,7 +145,7 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// # Ok::<(), faktory::Error>(()) /// }); /// ``` -pub struct Client { +pub struct Client { stream: S, opts: ClientOptions, } @@ -167,7 +167,7 @@ where impl Drop for Client where - S: AsyncBufRead + AsyncWrite + Unpin + Send, + S: AsyncWrite + Unpin + Send, { fn drop(&mut self) { tokio::task::block_in_place(|| { diff --git a/src/worker/mod.rs b/src/worker/mod.rs index 2309d62e..aaa20175 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -136,7 +136,7 @@ type CallbacksRegistry = FnvHashMap>; /// You can also register anything that implements [`JobRunner`] to handle jobs /// with [`register`](WorkerBuilder::register). /// -pub struct Worker { +pub struct Worker { c: Client, worker_states: Arc, callbacks: Arc>, @@ -149,7 +149,7 @@ impl Worker { } } -impl Worker { +impl Worker { async fn new(c: Client, workers_count: usize, callbacks: CallbacksRegistry) -> Self { Worker { c, From 56ccfb68a5e5b9928ba40c490c9e47f626a5d650 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 09:54:35 +0500 Subject: [PATCH 107/129] Store STATUS_TERMINATING in Fakotry signalled so --- src/worker/health.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/worker/health.rs b/src/worker/health.rs index 8e6f88da..849dd633 100644 --- a/src/worker/health.rs +++ b/src/worker/health.rs @@ -13,6 +13,15 @@ where S: AsyncBufRead + AsyncWrite + Reconnect + Send + Unpin + 'static, E: StdError + 'static + Send, { + /// Send beats to Fakotry and quiet/terminate workers if signalled so. + /// + /// Some core details: + /// - beats should be sent to Faktory at least every 15 seconds; + /// - a worker's lifecycle is "running -> quiet -> terminate"; + /// - STATUS_QUIET means the worker should not consume any new jobs, + /// but should _continue_ processing its current job (if any); + /// + /// Ref: https://github.com/contribsys/faktory/blob/main/server/workers.go#L21 pub(crate) async fn listen_for_heartbeats( &mut self, statuses: &[Arc], @@ -58,7 +67,7 @@ where // tell the workers to terminate // *and* fail the current job and immediately return for s in statuses { - s.store(STATUS_QUIET, atomic::Ordering::SeqCst); + s.store(STATUS_TERMINATING, atomic::Ordering::SeqCst); } break Ok(true); } From 44b8cfb9f6b7e96baf0fe55139e88b96b93a7d2a Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sat, 27 Apr 2024 09:55:06 +0500 Subject: [PATCH 108/129] Store STATUS_TERMINATING in Fakotry signalled so --- src/worker/health.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/worker/health.rs b/src/worker/health.rs index 849dd633..993dc904 100644 --- a/src/worker/health.rs +++ b/src/worker/health.rs @@ -14,13 +14,13 @@ where E: StdError + 'static + Send, { /// Send beats to Fakotry and quiet/terminate workers if signalled so. - /// + /// /// Some core details: /// - beats should be sent to Faktory at least every 15 seconds; /// - a worker's lifecycle is "running -> quiet -> terminate"; /// - STATUS_QUIET means the worker should not consume any new jobs, /// but should _continue_ processing its current job (if any); - /// + /// /// Ref: https://github.com/contribsys/faktory/blob/main/server/workers.go#L21 pub(crate) async fn listen_for_heartbeats( &mut self, From f07d9329ef4c4e882faf5f782e84c701eb74da0a Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 28 Apr 2024 15:36:46 +0500 Subject: [PATCH 109/129] Add PartialEq to proto::single::id module newtypes --- src/error.rs | 2 +- src/proto/single/id.rs | 11 ++++++++--- src/proto/single/mod.rs | 5 ++--- src/worker/runner.rs | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/error.rs b/src/error.rs index 13043243..fccdd0aa 100644 --- a/src/error.rs +++ b/src/error.rs @@ -171,7 +171,7 @@ pub enum TlsStream { #[error("underlying tls stream")] Native(#[source] tokio_native_tls::native_tls::Error), - /// Error in the underlying rutsls powered stream. + /// Error in the underlying rustls powered stream. #[cfg(feature = "rustls")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))] #[error("underlying tls stream")] diff --git a/src/proto/single/id.rs b/src/proto/single/id.rs index 9add5eb4..f1b6a844 100644 --- a/src/proto/single/id.rs +++ b/src/proto/single/id.rs @@ -1,5 +1,4 @@ use super::utils; -use std::fmt::Display; use std::ops::Deref; macro_rules! string_wrapper_impls { @@ -8,9 +7,9 @@ macro_rules! string_wrapper_impls { /// Create a new entity identifier. pub fn new(inner: S) -> Self where - S: AsRef + Clone + Display, + S: Into, { - Self(inner.to_string()) + Self(inner.into()) } } @@ -32,6 +31,12 @@ macro_rules! string_wrapper_impls { &self } } + + impl PartialEq for $new_type { + fn eq(&self, other: &str) -> bool { + self.deref().eq(other) + } + } }; } diff --git a/src/proto/single/mod.rs b/src/proto/single/mod.rs index a563e7ac..9e3905dc 100644 --- a/src/proto/single/mod.rs +++ b/src/proto/single/mod.rs @@ -2,7 +2,7 @@ use crate::Error; use chrono::{DateTime, Utc}; use derive_builder::Builder; use std::collections::HashMap; -use tokio::io::{AsyncBufRead, AsyncWrite}; +use tokio::io::{AsyncBufRead, AsyncWrite, AsyncWriteExt}; mod cmd; mod id; @@ -261,7 +261,6 @@ impl Job { } } -use tokio::io::AsyncWriteExt; pub async fn write_command( w: &mut W, command: &C, @@ -291,7 +290,7 @@ mod test { let job_args = vec!["ISBN-13:9781718501850"]; let job = JobBuilder::new(job_kind).args(job_args.clone()).build(); - assert!(job.jid != JobId::new("")); + assert!(&job.jid != ""); assert!(job.queue == JOB_DEFAULT_QUEUE.to_string()); assert_eq!(job.kind, job_kind); assert_eq!(job.args, job_args); diff --git a/src/worker/runner.rs b/src/worker/runner.rs index ba2e1872..47c13e49 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -89,7 +89,7 @@ where } } -/// A wrapper for the userland's handler. +/// A "transparent" wrapper for a handler. /// /// The `Closure` newtype is introduced to avoid having to box a job handler: /// we can now use `Closure(handler)` instead of `Box::new(handler)` and make @@ -98,7 +98,7 @@ where /// The `repr(transparent)` macro is to guarantee that this single-field struct /// and the wrapped handler have the same layout and so it is safe to operate on /// the in-memory representations of _the_ handler (submitted to us -/// from the userland) and its enclosed (by us) self. +/// from the user code) and its enclosed (by us) self. /// /// Ref: https://github.com/jonhoo/faktory-rs/pull/51 #[repr(transparent)] From cdb8caf532f9de5908ac2b1d2f1b1dc5169fa63c Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 28 Apr 2024 15:46:45 +0500 Subject: [PATCH 110/129] Do not require static string in tls::rustls --- src/tls/rustls.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/tls/rustls.rs b/src/tls/rustls.rs index a08299a4..53c85ed6 100644 --- a/src/tls/rustls.rs +++ b/src/tls/rustls.rs @@ -32,7 +32,7 @@ use tokio_rustls::TlsConnector; #[pin_project::pin_project] pub struct TlsStream { connector: TlsConnector, - hostname: &'static str, + hostname: String, #[pin] stream: RustlsStream, } @@ -82,7 +82,7 @@ impl TlsStream { }?; let host_and_port = utils::host_from_url(&url); let tcp_stream = TokioTcpStream::connect(&host_and_port).await?; - let host = url.host_str().unwrap().to_string().leak(); + let host = url.host_str().unwrap().to_string(); Ok(TlsStream::new(tcp_stream, connector, host).await?) } } @@ -95,7 +95,7 @@ where /// /// Internally creates a `ClientConfig` with an empty root certificates store and no client /// authentication. Use [`new`](TlsStream::new) for a customized `TlsConnector`. - pub async fn default(stream: S, hostname: &'static str) -> io::Result { + pub async fn default(stream: S, hostname: String) -> io::Result { let conf = ClientConfig::builder() .with_root_certificates(RootCertStore::empty()) .with_no_client_auth(); @@ -104,12 +104,11 @@ where } /// Create a new TLS connection on an existing stream with a non-default TLS configuration. - pub async fn new( - stream: S, - connector: TlsConnector, - hostname: &'static str, - ) -> io::Result { - let server_name = hostname.try_into().expect("a valid DNS name or IP address"); + pub async fn new(stream: S, connector: TlsConnector, hostname: String) -> io::Result { + let server_name = hostname + .clone() + .try_into() + .expect("a valid DNS name or IP address"); let tls_stream = connector .connect(server_name, stream) .await @@ -129,7 +128,7 @@ where { async fn reconnect(&mut self) -> io::Result { let stream = self.stream.get_mut().0.reconnect().await?; - TlsStream::new(stream, self.connector.clone(), self.hostname).await + TlsStream::new(stream, self.connector.clone(), self.hostname.clone()).await } } From fd231d1c2c70ba2d253c9ffc8c401bda2712c047 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 28 Apr 2024 15:52:28 +0500 Subject: [PATCH 111/129] Use permalink in Worker::listen_for_heartbeats docs --- src/worker/health.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker/health.rs b/src/worker/health.rs index 993dc904..053555ed 100644 --- a/src/worker/health.rs +++ b/src/worker/health.rs @@ -21,7 +21,7 @@ where /// - STATUS_QUIET means the worker should not consume any new jobs, /// but should _continue_ processing its current job (if any); /// - /// Ref: https://github.com/contribsys/faktory/blob/main/server/workers.go#L21 + /// See more details [here](https://github.com/contribsys/faktory/blob/b4a93227a3323ab4b1365b0c37c2fac4f9588cc8/server/workers.go#L13-L49). pub(crate) async fn listen_for_heartbeats( &mut self, statuses: &[Arc], From 15d171116ebda632a4ab61992fc672f06af19b6a Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 28 Apr 2024 17:27:02 +0500 Subject: [PATCH 112/129] Make WorkerBuilder return Self --- src/bin/loadtest.rs | 27 +++--- src/lib.rs | 13 +-- src/worker/builder.rs | 17 ++-- src/worker/mod.rs | 22 +++-- src/worker/runner.rs | 12 ++- tests/consumer.rs | 137 ++++++++++++++------------ tests/real/community.rs | 129 ++++++++++++++----------- tests/real/enterprise.rs | 203 +++++++++++++++++++++------------------ tests/tls/native_tls.rs | 26 ++--- tests/tls/rustls.rs | 26 ++--- 10 files changed, 335 insertions(+), 277 deletions(-) diff --git a/src/bin/loadtest.rs b/src/bin/loadtest.rs index aa1bec09..60d17d67 100644 --- a/src/bin/loadtest.rs +++ b/src/bin/loadtest.rs @@ -58,19 +58,20 @@ async fn main() { set.spawn(async move { // make producer and consumer let mut p = Client::connect(None).await.unwrap(); - let mut c = WorkerBuilder::default(); - c.register_fn("SomeJob", |_| { - Box::pin(async move { - let mut rng = rand::thread_rng(); - if rng.gen_bool(0.01) { - Err(io::Error::new(io::ErrorKind::Other, "worker closed")) - } else { - Ok(()) - } + let mut worker = WorkerBuilder::default() + .register_fn("SomeJob", |_| { + Box::pin(async move { + let mut rng = rand::thread_rng(); + if rng.gen_bool(0.01) { + Err(io::Error::new(io::ErrorKind::Other, "worker closed")) + } else { + Ok(()) + } + }) }) - }); - let mut c = c.connect(None).await.unwrap(); - + .connect(None) + .await + .unwrap(); let mut rng = rand::rngs::OsRng; let mut random_queues = Vec::from(QUEUES); random_queues.shuffle(&mut rng); @@ -89,7 +90,7 @@ async fn main() { } } else { // pop - c.run_one(0, &random_queues[..]).await?; + worker.run_one(0, &random_queues[..]).await?; if popped.fetch_add(1, atomic::Ordering::SeqCst) >= jobs { return Ok(idx); } diff --git a/src/lib.rs b/src/lib.rs index 57a4a0be..6f702741 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,12 +50,13 @@ //! # tokio_test::block_on(async { //! use faktory::WorkerBuilder; //! use std::io; -//! let mut w = WorkerBuilder::default(); -//! w.register_fn("foobar", |job| async move { -//! println!("{:?}", job); -//! Ok::<(), io::Error>(()) -//! }); -//! let mut w = w.connect(None).await.unwrap(); +//! let mut w = WorkerBuilder::default() +//! .register_fn("foobar", |job| async move { +//! println!("{:?}", job); +//! Ok::<(), io::Error>(()) +//! }) +//! .connect(None).await.unwrap(); +//! //! if let Err(e) = w.run(&["default"]).await { //! println!("worker failed: {}", e); //! } diff --git a/src/worker/builder.rs b/src/worker/builder.rs index 8bd15d03..f5768216 100644 --- a/src/worker/builder.rs +++ b/src/worker/builder.rs @@ -40,7 +40,7 @@ impl WorkerBuilder { /// Set the hostname to use for this worker. /// /// Defaults to the machine's hostname as reported by the operating system. - pub fn hostname(&mut self, hn: String) -> &mut Self { + pub fn hostname(mut self, hn: String) -> Self { self.opts.hostname = Some(hn); self } @@ -48,7 +48,7 @@ impl WorkerBuilder { /// Set a unique identifier for this worker. /// /// Defaults to a randomly generated 32-char ASCII string. - pub fn wid(&mut self, wid: WorkerId) -> &mut Self { + pub fn wid(mut self, wid: WorkerId) -> Self { self.opts.wid = Some(wid); self } @@ -60,7 +60,7 @@ impl WorkerBuilder { /// Note that calling this overrides the labels set previously. /// /// If you need to extend the labels already set, use [`WorkerBuilder::add_to_labels`] instead. - pub fn labels(&mut self, labels: I) -> &mut Self + pub fn labels(mut self, labels: I) -> Self where I: IntoIterator, { @@ -74,7 +74,7 @@ impl WorkerBuilder { /// if no labels have been explicitly set before - to the default `"rust"` label. /// /// If you need to override the labels set previously, use [`WorkerBuilder::labels`] instead. - pub fn add_to_labels(&mut self, labels: I) -> &mut Self + pub fn add_to_labels(mut self, labels: I) -> Self where I: IntoIterator, { @@ -85,7 +85,7 @@ impl WorkerBuilder { /// Set the number of workers to use `run` and `run_to_completion`. /// /// Defaults to 1. - pub fn workers(&mut self, w: usize) -> &mut Self { + pub fn workers(mut self, w: usize) -> Self { self.workers_count = w; self } @@ -97,14 +97,13 @@ impl WorkerBuilder { /// /// Note that only one single handler per job kind is supported. Registering another handler /// for the same job kind will silently override the handler registered previously. - pub fn register_fn(&mut self, kind: K, handler: H) -> &mut Self + pub fn register_fn(self, kind: K, handler: H) -> Self where K: Into, H: Fn(Job) -> Fut + Send + Sync + 'static, Fut: Future> + Send, { - self.register(kind, Closure(handler)); - self + self.register(kind, Closure(handler)) } /// Register a handler for the given job type (`kind`). @@ -114,7 +113,7 @@ impl WorkerBuilder { /// /// Note that only one single handler per job kind is supported. Registering another handler /// for the same job kind will silently override the handler registered previously. - pub fn register(&mut self, kind: K, runner: H) -> &mut Self + pub fn register(mut self, kind: K, runner: H) -> Self where K: Into, H: JobRunner + 'static, diff --git a/src/worker/mod.rs b/src/worker/mod.rs index aaa20175..ba93c449 100644 --- a/src/worker/mod.rs +++ b/src/worker/mod.rs @@ -110,11 +110,12 @@ type CallbacksRegistry = FnvHashMap>; /// Ok(()) /// } /// -/// let mut w = WorkerBuilder::default(); +/// let mut w = WorkerBuilder::default() +/// .register_fn("foo", process_job) +/// .connect(None) +/// .await +/// .unwrap(); /// -/// w.register_fn("foo", process_job); -/// -/// let mut w = w.connect(None).await.unwrap(); /// if let Err(e) = w.run(&["default"]).await { /// println!("worker failed: {}", e); /// } @@ -124,12 +125,17 @@ type CallbacksRegistry = FnvHashMap>; /// Handler can be inlined. /// /// ```no_run +/// # tokio_test::block_on(async { /// # use faktory::WorkerBuilder; /// # use std::io; -/// let mut w = WorkerBuilder::default(); -/// w.register_fn("bar", |job| async move { -/// println!("{:?}", job); -/// Ok::<(), io::Error>(()) +/// let _w = WorkerBuilder::default() +/// .register_fn("bar", |job| async move { +/// println!("{:?}", job); +/// Ok::<(), io::Error>(()) +/// }) +/// .connect(None) +/// .await +/// .unwrap(); /// }); /// ``` /// diff --git a/src/worker/runner.rs b/src/worker/runner.rs index 47c13e49..8d29c289 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -31,13 +31,17 @@ use std::future::Future; /// Ok(()) /// } /// } -/// -/// let mut w = WorkerBuilder::default(); +/// /// let handler = MyHandler { /// config: "bar".to_string(), /// }; -/// w.register("foo", handler); -/// let mut w = w.connect(None).await.unwrap(); +/// +/// let mut w = WorkerBuilder::default() +/// .register("foo", handler) +/// .connect(None) +/// .await +/// .unwrap(); +/// /// if let Err(e) = w.run(&["default"]).await { /// println!("worker failed: {}", e); /// } diff --git a/tests/consumer.rs b/tests/consumer.rs index b6ef494d..5fed5a54 100644 --- a/tests/consumer.rs +++ b/tests/consumer.rs @@ -63,8 +63,8 @@ use tokio::{spawn, time::sleep}; #[tokio::test(flavor = "multi_thread")] async fn hello() { let mut s = mock::Stream::default(); - let mut c: WorkerBuilder = WorkerBuilder::default(); - c.hostname("host".to_string()) + let w: Worker<_, io::Error> = WorkerBuilder::default() + .hostname("host".to_string()) .wid(WorkerId::new("wid")) .labels([ "will".to_string(), @@ -73,9 +73,11 @@ async fn hello() { ]) .labels(["foo".to_string(), "bar".to_string()]) .add_to_labels(["will".to_string()]) - .add_to_labels(["be".to_string(), "added".to_string()]); - c.register_fn("never_called", |_j: Job| async move { unreachable!() }); - let c = c.connect_with(s.clone(), None).await.unwrap(); + .add_to_labels(["be".to_string(), "added".to_string()]) + .register_fn("never_called", |_j: Job| async move { unreachable!() }) + .connect_with(s.clone(), None) + .await + .unwrap(); let written = s.pop_bytes_written(0); assert!(written.starts_with(b"HELLO {")); let written: serde_json::Value = serde_json::from_slice(&written[b"HELLO ".len()..]).unwrap(); @@ -90,7 +92,7 @@ async fn hello() { let labels = written["labels"].as_array().unwrap(); assert_eq!(labels, &["foo", "bar", "will", "be", "added"]); - drop(c); + drop(w); let written = s.pop_bytes_written(0); assert_eq!(written, b"END\r\n"); } @@ -98,10 +100,8 @@ async fn hello() { #[tokio::test(flavor = "multi_thread")] async fn hello_pwd() { let mut s = mock::Stream::with_salt(1545, "55104dc76695721d"); - - let mut c: WorkerBuilder = WorkerBuilder::default(); - c.register_fn("never_called", |_j: Job| async move { unreachable!() }); - let c = c + let w: Worker<_, io::Error> = WorkerBuilder::default() + .register_fn("never_called", |_j: Job| async move { unreachable!() }) .connect_with(s.clone(), Some("foobar".to_string())) .await .unwrap(); @@ -113,19 +113,20 @@ async fn hello_pwd() { written.get("pwdhash").and_then(|h| h.as_str()), Some("6d877f8e5544b1f2598768f817413ab8a357afffa924dedae99eb91472d4ec30") ); - - drop(c); + drop(w); } #[tokio::test(flavor = "multi_thread")] async fn dequeue() { let mut s = mock::Stream::default(); - let mut c = WorkerBuilder::default(); - c.register_fn("foobar", |job: Job| async move { - assert_eq!(job.args(), &["z"]); - Ok::<(), io::Error>(()) - }); - let mut c = c.connect_with(s.clone(), None).await.unwrap(); + let mut w = WorkerBuilder::default() + .register_fn("foobar", |job: Job| async move { + assert_eq!(job.args(), &["z"]); + Ok::<(), io::Error>(()) + }) + .connect_with(s.clone(), None) + .await + .unwrap(); s.ignore(0); s.push_bytes_to_read( @@ -143,7 +144,7 @@ async fn dequeue() { }\r\n", ); s.ok(0); // for the ACK - if let Err(e) = c.run_one(0, &["default"]).await { + if let Err(e) = w.run_one(0, &["default"]).await { println!("{:?}", e); unreachable!(); } @@ -159,12 +160,14 @@ async fn dequeue() { #[tokio::test(flavor = "multi_thread")] async fn dequeue_first_empty() { let mut s = mock::Stream::default(); - let mut c = WorkerBuilder::default(); - c.register_fn("foobar", |job: Job| async move { - assert_eq!(job.args(), &["z"]); - Ok::<(), io::Error>(()) - }); - let mut c = c.connect_with(s.clone(), None).await.unwrap(); + let mut w = WorkerBuilder::default() + .register_fn("foobar", |job: Job| async move { + assert_eq!(job.args(), &["z"]); + Ok::<(), io::Error>(()) + }) + .connect_with(s.clone(), None) + .await + .unwrap(); s.ignore(0); s.push_bytes_to_read( @@ -184,7 +187,7 @@ async fn dequeue_first_empty() { s.ok(0); // for the ACK // run once, shouldn't do anything - match c.run_one(0, &["default"]).await { + match w.run_one(0, &["default"]).await { Ok(did_work) => assert!(!did_work), Err(e) => { println!("{:?}", e); @@ -192,7 +195,7 @@ async fn dequeue_first_empty() { } } // run again, this time doing the job - match c.run_one(0, &["default"]).await { + match w.run_one(0, &["default"]).await { Ok(did_work) => assert!(did_work), Err(e) => { println!("{:?}", e); @@ -214,14 +217,16 @@ async fn dequeue_first_empty() { #[tokio::test(flavor = "multi_thread")] async fn well_behaved() { let mut s = mock::Stream::new(2); // main plus worker - let mut c = WorkerBuilder::default(); - c.wid(WorkerId::new("wid")); - c.register_fn("foobar", |_| async move { - // NOTE: this time needs to be so that it lands between the first heartbeat and the second - sleep(Duration::from_secs(7)).await; - Ok::<(), io::Error>(()) - }); - let mut c = c.connect_with(s.clone(), None).await.unwrap(); + let mut w = WorkerBuilder::default() + .wid(WorkerId::new("wid")) + .register_fn("foobar", |_| async move { + // NOTE: this time needs to be so that it lands between the first heartbeat and the second + sleep(Duration::from_secs(7)).await; + Ok::<(), io::Error>(()) + }) + .connect_with(s.clone(), None) + .await + .unwrap(); s.ignore(0); // push a job that'll take a while to run @@ -240,7 +245,7 @@ async fn well_behaved() { }\r\n", ); - let jh = spawn(async move { c.run(&["default"]).await }); + let jh = spawn(async move { w.run(&["default"]).await }); // the running thread won't return for a while. the heartbeat thingy is going to eventually // send a heartbeat, and we want to respond to that with a "quiet" to make it not accept any @@ -279,14 +284,16 @@ async fn well_behaved() { #[tokio::test(flavor = "multi_thread")] async fn no_first_job() { let mut s = mock::Stream::new(2); // main plus worker - let mut c = WorkerBuilder::default(); - c.wid(WorkerId::new("wid")); - c.register_fn("foobar", |_| async move { - // NOTE: this time needs to be so that it lands between the first heartbeat and the second - sleep(Duration::from_secs(7)).await; - Ok::<(), io::Error>(()) - }); - let mut c = c.connect_with(s.clone(), None).await.unwrap(); + let mut w = WorkerBuilder::default() + .wid(WorkerId::new("wid")) + .register_fn("foobar", |_| async move { + // NOTE: this time needs to be so that it lands between the first heartbeat and the second + sleep(Duration::from_secs(7)).await; + Ok::<(), io::Error>(()) + }) + .connect_with(s.clone(), None) + .await + .unwrap(); s.ignore(0); // push a job that'll take a while to run @@ -305,7 +312,7 @@ async fn no_first_job() { }\r\n", ); - let jh = spawn(async move { c.run(&["default"]).await }); + let jh = spawn(async move { w.run(&["default"]).await }); // the running thread won't return for a while. the heartbeat thingy is going to eventually // send a heartbeat, and we want to respond to that with a "quiet" to make it not accept any @@ -345,15 +352,17 @@ async fn no_first_job() { #[tokio::test(flavor = "multi_thread")] async fn well_behaved_many() { let mut s = mock::Stream::new(3); // main plus 2 workers - let mut w = WorkerBuilder::default(); - w.workers(2); - w.wid(WorkerId::new("wid")); - w.register_fn("foobar", |_| async move { - // NOTE: this time needs to be so that it lands between the first heartbeat and the second - sleep(Duration::from_secs(7)).await; - Ok::<(), io::Error>(()) - }); - let mut w = w.connect_with(s.clone(), None).await.unwrap(); + let mut w = WorkerBuilder::default() + .workers(2) + .wid(WorkerId::new("wid")) + .register_fn("foobar", |_| async move { + // NOTE: this time needs to be so that it lands between the first heartbeat and the second + sleep(Duration::from_secs(7)).await; + Ok::<(), io::Error>(()) + }) + .connect_with(s.clone(), None) + .await + .unwrap(); s.ignore(0); // push two jobs that'll take a while to run @@ -426,16 +435,18 @@ async fn terminate() { let mut s = mock::Stream::new(2); // main plus worker // prepare a worker with only never (!) returning handler - let mut w: WorkerBuilder = WorkerBuilder::default(); - w.hostname("machine".into()); - w.wid(WorkerId::new("wid")); - w.register_fn("foobar", |_| async move { - loop { - sleep(Duration::from_secs(5)).await; - } - }); + let mut w: Worker<_, io::Error> = WorkerBuilder::default() + .hostname("machine".into()) + .wid(WorkerId::new("wid")) + .register_fn("foobar", |_| async move { + loop { + sleep(Duration::from_secs(5)).await; + } + }) + .connect_with(s.clone(), None) + .await + .unwrap(); - let mut w = w.connect_with(s.clone(), None).await.unwrap(); // what now is being ignored on `mine` channel are these written bytes (pid will vary): // b"HELLO {\"hostname\":\"machine\",\"wid\":\"wid\",\"pid\":7332,\"labels\":[\"rust\"],\"v\":2}\r\n" // this was the HELLO from main (coordinating) worker diff --git a/tests/real/community.rs b/tests/real/community.rs index b2157b07..44100cb4 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -13,12 +13,14 @@ async fn hello_client() { #[tokio::test(flavor = "multi_thread")] async fn hello_worker() { skip_check!(); - let mut c = WorkerBuilder::::default(); - c.hostname("tester".to_string()) - .labels(vec!["foo".to_string(), "bar".to_string()]); - c.register_fn("never_called", |_| async move { unreachable!() }); - let c = c.connect(None).await.unwrap(); - drop(c); + let w = WorkerBuilder::::default() + .hostname("tester".to_string()) + .labels(vec!["foo".to_string(), "bar".to_string()]) + .register_fn("never_called", |_| async move { unreachable!() }) + .connect(None) + .await + .unwrap(); + drop(w); } #[tokio::test(flavor = "multi_thread")] @@ -35,8 +37,7 @@ async fn roundtrip() { let local = "roundtrip"; let jid = JobId::new("x-job-id-0123456782"); - let mut worker = WorkerBuilder::default(); - worker + let mut worker = WorkerBuilder::default() .labels(vec!["rust".into(), local.into()]) .workers(1) .wid(WorkerId::random()) @@ -46,8 +47,11 @@ async fn roundtrip() { assert_eq!(job.args(), &[Value::from("ISBN-13:9781718501850")]); Ok::<(), io::Error>(()) }) - .register_fn("image", |_| async move { unreachable!() }); - let mut worker = worker.connect(None).await.unwrap(); + .register_fn("image", |_| async move { unreachable!() }) + .connect(None) + .await + .unwrap(); + let mut client = Client::connect(None).await.unwrap(); client .enqueue( @@ -73,18 +77,20 @@ async fn multi() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); - let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(WorkerId::new(local)); - c.register_fn(local, move |j| { - let tx = sync::Arc::clone(&tx); - Box::pin(async move { - tx.lock().unwrap().send(j).unwrap(); - Ok::<(), io::Error>(()) + let mut w = WorkerBuilder::default() + .hostname("tester".to_string()) + .wid(WorkerId::new(local)) + .register_fn(local, move |j| { + let tx = sync::Arc::clone(&tx); + Box::pin(async move { + tx.lock().unwrap().send(j).unwrap(); + Ok::<(), io::Error>(()) + }) }) - }); - - let mut c = c.connect(None).await.unwrap(); + .connect(None) + .await + .unwrap(); let mut p = Client::connect(None).await.unwrap(); p.enqueue(Job::new(local, vec![Value::from(1), Value::from("foo")]).on_queue(local)) @@ -94,13 +100,13 @@ async fn multi() { .await .unwrap(); - c.run_one(0, &[local]).await.unwrap(); + w.run_one(0, &[local]).await.unwrap(); let job = rx.recv().unwrap(); assert_eq!(job.queue, local); assert_eq!(job.kind(), local); assert_eq!(job.args(), &[Value::from(1), Value::from("foo")]); - c.run_one(0, &[local]).await.unwrap(); + w.run_one(0, &[local]).await.unwrap(); let job = rx.recv().unwrap(); assert_eq!(job.queue, local); assert_eq!(job.kind(), local); @@ -114,18 +120,20 @@ async fn fail() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); - let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(WorkerId::new(local)); - - c.register_fn(local, move |j| { - let tx = sync::Arc::clone(&tx); - Box::pin(async move { - tx.lock().unwrap().send(j).unwrap(); - Err(io::Error::new(io::ErrorKind::Other, "nope")) - }) - }); - let mut c = c.connect(None).await.unwrap(); + let mut w = WorkerBuilder::default() + .hostname("tester".to_string()) + .wid(WorkerId::new(local)) + .register_fn(local, move |j| { + let tx = sync::Arc::clone(&tx); + Box::pin(async move { + tx.lock().unwrap().send(j).unwrap(); + Err(io::Error::new(io::ErrorKind::Other, "nope")) + }) + }) + .connect(None) + .await + .unwrap(); let mut p = Client::connect(None).await.unwrap(); @@ -137,9 +145,9 @@ async fn fail() { .await .unwrap(); - c.run_one(0, &[local]).await.unwrap(); - c.run_one(0, &[local]).await.unwrap(); - drop(c); + w.run_one(0, &[local]).await.unwrap(); + w.run_one(0, &[local]).await.unwrap(); + drop(w); assert_eq!(rx.into_iter().take(2).count(), 2); } @@ -151,13 +159,16 @@ async fn queue() { let (tx, rx) = sync::mpsc::channel(); let tx = sync::Arc::new(sync::Mutex::new(tx)); - let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(WorkerId::new(local)); - c.register_fn(local, move |_job| { - let tx = sync::Arc::clone(&tx); - Box::pin(async move { tx.lock().unwrap().send(true) }) - }); - let mut c = c.connect(None).await.unwrap(); + let mut w = WorkerBuilder::default() + .hostname("tester".to_string()) + .wid(WorkerId::new(local)) + .register_fn(local, move |_job| { + let tx = sync::Arc::clone(&tx); + Box::pin(async move { tx.lock().unwrap().send(true) }) + }) + .connect(None) + .await + .unwrap(); let mut p = Client::connect(None).await.unwrap(); p.enqueue(Job::new(local, vec![Value::from(1)]).on_queue(local)) @@ -165,14 +176,14 @@ async fn queue() { .unwrap(); p.queue_pause(&[local]).await.unwrap(); - let had_job = c.run_one(0, &[local]).await.unwrap(); + let had_job = w.run_one(0, &[local]).await.unwrap(); assert!(!had_job); let worker_executed = rx.try_recv().is_ok(); assert!(!worker_executed); p.queue_resume(&[local]).await.unwrap(); - let had_job = c.run_one(0, &[local]).await.unwrap(); + let had_job = w.run_one(0, &[local]).await.unwrap(); assert!(had_job); let worker_executed = rx.try_recv().is_ok(); assert!(worker_executed); @@ -248,13 +259,16 @@ async fn test_jobs_pushed_in_bulk() { // Let's check that the two well-formatted jobs // have _really_ been enqueued, i.e. that `enqueue_many` // is not an all-or-nothing operation: - let mut c = WorkerBuilder::default(); - c.hostname("tester".to_string()).wid(WorkerId::new(local_3)); - c.register_fn("very_special", move |_job| async { - Ok::<(), io::Error>(()) - }); - c.register_fn("broken", move |_job| async { Ok::<(), io::Error>(()) }); - let mut c = c.connect(None).await.unwrap(); + let mut c = WorkerBuilder::default() + .hostname("tester".to_string()) + .wid(WorkerId::new(local_3)) + .register_fn("very_special", move |_job| async { + Ok::<(), io::Error>(()) + }) + .register_fn("broken", move |_job| async { Ok::<(), io::Error>(()) }) + .connect(None) + .await + .unwrap(); // we targeted "very_special" jobs to "local_4" queue assert!(c.run_one(0, &[local_4]).await.unwrap()); @@ -282,11 +296,12 @@ async fn test_jobs_created_with_builder() { // prepare a client and a worker: let mut cl = Client::connect(None).await.unwrap(); - let mut w = WorkerBuilder::default(); - w.register_fn("rebuild_index", assert_args_empty); - w.register_fn("register_order", assert_args_not_empty); - - let mut w = w.connect(None).await.unwrap(); + let mut w = WorkerBuilder::default() + .register_fn("rebuild_index", assert_args_empty) + .register_fn("register_order", assert_args_not_empty) + .connect(None) + .await + .unwrap(); // prepare some jobs with JobBuilder: let job1 = JobBuilder::new("rebuild_index") diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index db6dda42..be261d4f 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -42,9 +42,11 @@ async fn ent_expiring_job() { // prepare a client and a worker: let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut c = WorkerBuilder::default(); - c.register_fn("AnExpiringJob", print_job); - let mut c = c.connect(Some(&url)).await.unwrap(); + let mut w = WorkerBuilder::default() + .register_fn("AnExpiringJob", print_job) + .connect(Some(&url)) + .await + .unwrap(); // prepare an expiring job: let job_ttl_secs: u64 = 3; @@ -58,10 +60,10 @@ async fn ent_expiring_job() { // enqueue and fetch immediately job1: p.enqueue(job1).await.unwrap(); - assert_had_one!(&mut c, "ent_expiring_job"); + assert_had_one!(&mut w, "ent_expiring_job"); // check that the queue is drained: - assert_is_empty!(&mut c, "ent_expiring_job"); + assert_is_empty!(&mut w, "ent_expiring_job"); // prepare another one: let job2 = JobBuilder::new("AnExpiringJob") @@ -75,7 +77,7 @@ async fn ent_expiring_job() { tokio::time::sleep(time::Duration::from_secs(job_ttl_secs * 2)).await; // For the non-enterprise edition of Faktory, this assertion will // fail, which should be taken into account when running the test suite on CI. - assert_is_empty!(&mut c, local); + assert_is_empty!(&mut w, local); } #[tokio::test(flavor = "multi_thread")] @@ -91,9 +93,11 @@ async fn ent_unique_job() { // prepare client and worker: let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut c = WorkerBuilder::default(); - c.register_fn(job_type, print_job); - let mut c = c.connect(Some(&url)).await.unwrap(); + let mut w = WorkerBuilder::default() + .register_fn(job_type, print_job) + .connect(Some(&url)) + .await + .unwrap(); // Reminder. Jobs are considered unique for kind + args + queue. // So the following two jobs, will be accepted by Faktory, since we @@ -113,11 +117,11 @@ async fn ent_unique_job() { p.enqueue(job2).await.unwrap(); - let had_job = c.run_one(0, &[queue_name]).await.unwrap(); + let had_job = w.run_one(0, &[queue_name]).await.unwrap(); assert!(had_job); - let had_another_one = c.run_one(0, &[queue_name]).await.unwrap(); + let had_another_one = w.run_one(0, &[queue_name]).await.unwrap(); assert!(had_another_one); - let and_that_is_it_for_now = !c.run_one(0, &[queue_name]).await.unwrap(); + let and_that_is_it_for_now = !w.run_one(0, &[queue_name]).await.unwrap(); assert!(and_that_is_it_for_now); // let's now create a unique job and followed by a job with @@ -148,13 +152,13 @@ async fn ent_unique_job() { } // Let's now consume the job which is 'holding' a unique lock: - let had_job = c.run_one(0, &[queue_name]).await.unwrap(); + let had_job = w.run_one(0, &[queue_name]).await.unwrap(); assert!(had_job); // And check that the queue is really empty (`job2` from above // has not been queued indeed): - let queue_is_empty = !c.run_one(0, &[queue_name]).await.unwrap(); + let queue_is_empty = !w.run_one(0, &[queue_name]).await.unwrap(); assert!(queue_is_empty); @@ -176,10 +180,10 @@ async fn ent_unique_job() { // ... so the server will accept it: p.enqueue(job2).await.unwrap(); - assert_had_one!(&mut c, queue_name); - assert_had_one!(&mut c, queue_name); + assert_had_one!(&mut w, queue_name); + assert_had_one!(&mut w, queue_name); // and the queue is empty again: - assert_is_empty!(&mut c, queue_name); + assert_is_empty!(&mut w, queue_name); } #[tokio::test(flavor = "multi_thread")] @@ -207,21 +211,23 @@ async fn ent_unique_job_until_success() { // will sleep for a corresponding period of time, pretending // to work hard: let mut client_a = Client::connect(Some(&url1)).await.unwrap(); - let mut worker_a = WorkerBuilder::default(); - worker_a.register_fn(job_type, |job| async move { - let args = job.args().to_owned(); - let mut args = args.iter(); - let diffuculty_level = args - .next() - .expect("job difficulty level is there") - .to_owned(); - let sleep_secs = - serde_json::from_value::(diffuculty_level).expect("a valid number"); - time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; - eprintln!("{:?}", job); - Ok::<(), io::Error>(()) - }); - let mut worker_a = worker_a.connect(Some(&url1)).await.unwrap(); + let mut worker_a = WorkerBuilder::default() + .register_fn(job_type, |job| async move { + let args = job.args().to_owned(); + let mut args = args.iter(); + let diffuculty_level = args + .next() + .expect("job difficulty level is there") + .to_owned(); + let sleep_secs = + serde_json::from_value::(diffuculty_level).expect("a valid number"); + time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; + eprintln!("{:?}", job); + Ok::<(), io::Error>(()) + }) + .connect(Some(&url1)) + .await + .unwrap(); let job = JobBuilder::new(job_type) .args(vec![difficulty_level]) .queue(queue_name) @@ -287,21 +293,23 @@ async fn ent_unique_job_until_start() { let url1 = url.clone(); let handle = tokio::spawn(async move { let mut client_a = Client::connect(Some(&url1)).await.unwrap(); - let mut worker_a = WorkerBuilder::default(); - worker_a.register_fn(job_type, |job| async move { - let args = job.args().to_owned(); - let mut args = args.iter(); - let diffuculty_level = args - .next() - .expect("job difficulty level is there") - .to_owned(); - let sleep_secs = - serde_json::from_value::(diffuculty_level).expect("a valid number"); - time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; - eprintln!("{:?}", job); - Ok::<(), io::Error>(()) - }); - let mut worker_a = worker_a.connect(Some(&url1)).await.unwrap(); + let mut worker_a = WorkerBuilder::default() + .register_fn(job_type, |job| async move { + let args = job.args().to_owned(); + let mut args = args.iter(); + let diffuculty_level = args + .next() + .expect("job difficulty level is there") + .to_owned(); + let sleep_secs = + serde_json::from_value::(diffuculty_level).expect("a valid number"); + time::sleep(time::Duration::from_secs(sleep_secs as u64)).await; + eprintln!("{:?}", job); + Ok::<(), io::Error>(()) + }) + .connect(Some(&url1)) + .await + .unwrap(); client_a .enqueue( JobBuilder::new(job_type) @@ -377,9 +385,11 @@ async fn ent_unique_job_bypass_unique_lock() { // let's consume three times from the queue to verify that the first two jobs // have been enqueued for real, while the last one has not. - let mut c = WorkerBuilder::default(); - c.register_fn("order", print_job); - let mut c = c.connect(Some(&url)).await.unwrap(); + let mut c = WorkerBuilder::default() + .register_fn("order", print_job) + .connect(Some(&url)) + .await + .unwrap(); assert!(c.run_one(0, &[queue_name]).await.unwrap()); assert!(c.run_one(0, &[queue_name]).await.unwrap()); @@ -422,14 +432,12 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { p.enqueue(job_tackable).await.expect("enqueued"); - let mut c = WorkerBuilder::default(); - - { - let job_id = job_id.clone(); - let url = url.clone(); - c.register_fn("order", move |job| { - let job_id = job_id.clone(); - let url = url.clone(); + let url_copy = url.clone(); + let job_id_copy = job_id.clone(); + let mut c = WorkerBuilder::default() + .register_fn("order", move |job| { + let job_id = job_id_copy.clone(); + let url = url_copy.clone(); Box::pin(async move { let mut t = Client::connect(Some(&url)) .await @@ -473,9 +481,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { // considering the job done Ok::<(), io::Error>(eprintln!("{:?}", job)) }) - }); - } - let mut c = c + }) .connect(Some(&url)) .await .expect("Successfully ran a handshake with 'Faktory'"); @@ -557,10 +563,12 @@ async fn test_batch_of_jobs_can_be_initiated() { let url = learn_faktory_url(); let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut c = WorkerBuilder::default(); - c.register_fn("thumbnail", |_job| async { Ok::<(), io::Error>(()) }); - c.register_fn("clean_up", |_job| async { Ok(()) }); - let mut c = c.connect(Some(&url)).await.unwrap(); + let mut w = WorkerBuilder::default() + .register_fn("thumbnail", |_job| async { Ok::<(), io::Error>(()) }) + .register_fn("clean_up", |_job| async { Ok(()) }) + .connect(Some(&url)) + .await + .unwrap(); let mut t = Client::connect(Some(&url)) .await .expect("job progress tracker created successfully"); @@ -622,9 +630,9 @@ async fn test_batch_of_jobs_can_be_initiated() { assert_eq!(s.complete_callback_state, CallbackState::Pending); // consume and execute job 1 ... - assert_had_one!(&mut c, "test_batch_of_jobs_can_be_initiated"); + assert_had_one!(&mut w, "test_batch_of_jobs_can_be_initiated"); // ... and try consuming from the "callback" queue: - assert_is_empty!(&mut c, "test_batch_of_jobs_can_be_initiated__CALLBACKs"); + assert_is_empty!(&mut w, "test_batch_of_jobs_can_be_initiated__CALLBACKs"); // let's ask the Faktory server about the batch status after // we have consumed one job from this batch: @@ -640,9 +648,9 @@ async fn test_batch_of_jobs_can_be_initiated() { assert_eq!(s.failed, 0); // now, consume and execute job 2 - assert_had_one!(&mut c, "test_batch_of_jobs_can_be_initiated"); + assert_had_one!(&mut w, "test_batch_of_jobs_can_be_initiated"); // ... and check the callback queue again: - assert_is_empty!(&mut c, "test_batch_of_jobs_can_be_initiated__CALLBACKs"); // not just yet ... + assert_is_empty!(&mut w, "test_batch_of_jobs_can_be_initiated__CALLBACKs"); // not just yet ... // let's check batch status once again: let s = t @@ -657,7 +665,7 @@ async fn test_batch_of_jobs_can_be_initiated() { assert_eq!(s.failed, 0); // finally, consume and execute job 3 - the last one from the batch - assert_had_one!(&mut c, "test_batch_of_jobs_can_be_initiated"); + assert_had_one!(&mut w, "test_batch_of_jobs_can_be_initiated"); // let's check batch status to see what happens after // all the jobs from the batch have been executed: @@ -674,7 +682,7 @@ async fn test_batch_of_jobs_can_be_initiated() { assert_eq!(s.complete_callback_state, CallbackState::Enqueued); // let's now successfully consume from the "callback" queue: - assert_had_one!(&mut c, "test_batch_of_jobs_can_be_initiated__CALLBACKs"); + assert_had_one!(&mut w, "test_batch_of_jobs_can_be_initiated__CALLBACKs"); // let's check batch status one last time: let s = t @@ -694,9 +702,11 @@ async fn test_batches_can_be_nested() { // Set up 'client', 'worker', and 'tracker': let mut p = Client::connect(Some(&url)).await.unwrap(); - let mut c = WorkerBuilder::default(); - c.register_fn("jobtype", |_job| async { Ok::<(), io::Error>(()) }); - let mut _c = c.connect(Some(&url)).await.unwrap(); + let _w = WorkerBuilder::default() + .register_fn("jobtype", |_job| async { Ok::<(), io::Error>(()) }) + .connect(Some(&url)) + .await + .unwrap(); let mut t = Client::connect(Some(&url)) .await .expect("job progress tracker created successfully"); @@ -715,7 +725,7 @@ async fn test_batches_can_be_nested() { .queue("test_batches_can_be_nested") .build(); - // Sccording to Faktory docs: + // According to Faktory docs: // "The callback for a parent batch will not enqueue until the callback for the child batch has finished." // See: https://github.com/contribsys/faktory/wiki/Ent-Batches#guarantees let parent_cb_job = Job::builder("clean_up") @@ -795,10 +805,12 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { // prepare a client, a worker of 'order' jobs, and a tracker: let mut cl = Client::connect(Some(&url)).await.unwrap(); let mut tr = Client::connect(Some(&url)).await.unwrap(); - let mut w = WorkerBuilder::default(); - w.register_fn("order", |_job| async { Ok(()) }); - w.register_fn("order_clean_up", |_job| async { Ok::<(), io::Error>(()) }); - let mut c = w.connect(Some(&url)).await.unwrap(); + let mut w = WorkerBuilder::default() + .register_fn("order", |_job| async { Ok(()) }) + .register_fn("order_clean_up", |_job| async { Ok::<(), io::Error>(()) }) + .connect(Some(&url)) + .await + .unwrap(); let mut jobs = some_jobs( "order", @@ -836,14 +848,14 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { // consume those 3 jobs successfully; for _ in 0..3 { assert_had_one!( - &mut c, + &mut w, "test_callback_will_not_be_queued_unless_batch_gets_committed" ); } // verify the queue is drained: assert_is_empty!( - &mut c, + &mut w, "test_callback_will_not_be_queued_unless_batch_gets_committed" ); @@ -856,7 +868,7 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { // to double-check, let's assert the success callbacks queue is empty: assert_is_empty!( - &mut c, + &mut w, "test_callback_will_not_be_queued_unless_batch_gets_committed__CALLBACKs" ); @@ -869,7 +881,7 @@ async fn test_callback_will_not_be_queued_unless_batch_gets_committed() { // finally, let's consume from the success callbacks queue ... assert_had_one!( - &mut c, + &mut w, "test_callback_will_not_be_queued_unless_batch_gets_committed__CALLBACKs" ); @@ -927,18 +939,19 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { assert_eq!(s.complete_callback_state, CallbackState::Enqueued); assert_eq!(s.success_callback_state, CallbackState::Pending); - let mut c = WorkerBuilder::default(); - c.register_fn(complete_cb_jobtype, |_job| async { Ok(()) }); - c.register_fn(success_cb_jobtype, |_job| async { - Err(io::Error::new( - io::ErrorKind::Other, - "we want this one to fail to test the 'CallbackState' behavior", - )) - }); - - let mut c = c.connect(Some(&url)).await.unwrap(); + let mut w = WorkerBuilder::default() + .register_fn(complete_cb_jobtype, |_job| async { Ok(()) }) + .register_fn(success_cb_jobtype, |_job| async { + Err(io::Error::new( + io::ErrorKind::Other, + "we want this one to fail to test the 'CallbackState' behavior", + )) + }) + .connect(Some(&url)) + .await + .unwrap(); - assert_had_one!(&mut c, q_name); // complete callback consumed + assert_had_one!(&mut w, q_name); // complete callback consumed let s = tracker .get_batch_status(bid.clone()) @@ -954,7 +967,7 @@ async fn test_callback_will_be_queued_upon_commit_even_if_batch_is_empty() { CallbackState::Enqueued => {} _ => panic!("Expected the callback to have been enqueued, since the `complete` callback has already executed"), } - assert_had_one!(&mut c, q_name); // success callback consumed + assert_had_one!(&mut w, q_name); // success callback consumed let s = tracker .get_batch_status(bid.clone()) diff --git a/tests/tls/native_tls.rs b/tests/tls/native_tls.rs index b159c959..582a6871 100644 --- a/tests/tls/native_tls.rs +++ b/tests/tls/native_tls.rs @@ -19,15 +19,8 @@ async fn roundtrip_tls() { if env::var_os("FAKTORY_URL_SECURE").is_none() { return; } - let local = "roundtrip_tls"; - let (tx, rx) = sync::mpsc::channel(); - let mut c = WorkerBuilder::default(); - - c.hostname("tester".to_string()).wid(WorkerId::new(local)); - c.register(local, fixtures::JobHandler::new(tx)); - let tls = || async { let connector = TlsConnector::builder() .danger_accept_invalid_certs(true) @@ -38,12 +31,23 @@ async fn roundtrip_tls() { .unwrap() }; - let mut c = c.connect_with(tls().await, None).await.unwrap(); - let mut p = Client::connect_with(tls().await, None).await.unwrap(); - p.enqueue(Job::new(local, vec!["z"]).on_queue(local)) + let mut worker = WorkerBuilder::default() + .hostname("tester".to_string()) + .wid(WorkerId::new(local)) + .register(local, fixtures::JobHandler::new(tx)) + .connect_with(tls().await, None) .await .unwrap(); - c.run_one(0, &[local]).await.unwrap(); + + // "one-shot" client + Client::connect_with(tls().await, None) + .await + .unwrap() + .enqueue(Job::new(local, vec!["z"]).on_queue(local)) + .await + .unwrap(); + + worker.run_one(0, &[local]).await.unwrap(); let job = rx.recv().unwrap(); assert_eq!(job.queue, local); diff --git a/tests/tls/rustls.rs b/tests/tls/rustls.rs index a7fa428b..5d6e627b 100644 --- a/tests/tls/rustls.rs +++ b/tests/tls/rustls.rs @@ -21,15 +21,8 @@ async fn roundtrip_tls() { if env::var_os("FAKTORY_URL_SECURE").is_none() { return; } - let local = "roundtrip_tls"; - let (tx, rx) = sync::mpsc::channel(); - let mut c = WorkerBuilder::default(); - - c.hostname("tester".to_string()).wid(WorkerId::new(local)); - c.register(local, fixtures::JobHandler::new(tx)); - let tls = || async { let verifier = fixtures::TestServerCertVerifier::new( SignatureScheme::RSA_PSS_SHA512, @@ -52,12 +45,23 @@ async fn roundtrip_tls() { .unwrap() }; - let mut c = c.connect_with(tls().await, None).await.unwrap(); - let mut p = Client::connect_with(tls().await, None).await.unwrap(); - p.enqueue(Job::new(local, vec!["z"]).on_queue(local)) + let mut worker = WorkerBuilder::default() + .hostname("tester".to_string()) + .wid(WorkerId::new(local)) + .register(local, fixtures::JobHandler::new(tx)) + .connect_with(tls().await, None) + .await + .unwrap(); + + // "one-shot" client + Client::connect_with(tls().await, None) + .await + .unwrap() + .enqueue(Job::new(local, vec!["z"]).on_queue(local)) .await .unwrap(); - c.run_one(0, &[local]).await.unwrap(); + + worker.run_one(0, &[local]).await.unwrap(); let job = rx.recv().unwrap(); assert_eq!(job.queue, local); From 7724d2472c2beeb8eb8b714ec27071c07dd6d9ed Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 28 Apr 2024 17:31:13 +0500 Subject: [PATCH 113/129] Run cargo fmt on sources --- src/worker/runner.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/worker/runner.rs b/src/worker/runner.rs index 8d29c289..e097bf16 100644 --- a/src/worker/runner.rs +++ b/src/worker/runner.rs @@ -31,17 +31,17 @@ use std::future::Future; /// Ok(()) /// } /// } -/// +/// /// let handler = MyHandler { /// config: "bar".to_string(), /// }; -/// +/// /// let mut w = WorkerBuilder::default() /// .register("foo", handler) /// .connect(None) /// .await /// .unwrap(); -/// +/// /// if let Err(e) = w.run(&["default"]).await { /// println!("worker failed: {}", e); /// } From 46e545754324647b6f42b9ac9905996bde3650a7 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 28 Apr 2024 18:02:51 +0500 Subject: [PATCH 114/129] Make min version pass --- Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6a4e8417..a2720c70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,10 @@ native-tls = { version = "0.2.4", optional = true } num-bigint = "0.4.2" oid-registry = "0.6.1" openssl = { version = "0.10.60", optional = true } -rustls = "0.22.1" + +# TryFrom for ServerName<'static> has been implemented: +# https://github.com/rustls/pki-types/compare/rustls:3793627...rustls:1303efa# +rustls-pki-types = { version = "1.0.1", optional = true } # Lockstep between `serde` and `serde_derive` was introduced with the "pinned" release: # https://github.com/serde-rs/serde/compare/v1.0.185...v1.0.186#diff-2843fc1320fa24a059f5ca967ee45d116110116263a8ba311a3aca3793c562f0R34-R41 From 57c958c02a17ae8834fb041b64872245fb7db925 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 28 Apr 2024 18:04:10 +0500 Subject: [PATCH 115/129] Make min version pass. Re-gen lockfile --- Cargo.lock | 161 ++++++++++++++++++++++++++++------------------------- 1 file changed, 84 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2ae48b8..3b192f33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,18 +138,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "async-trait" -version = "0.1.79" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -196,9 +196,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytes" @@ -208,9 +208,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.91" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd97381a8cc6493395a5afc4c691c1084b3768db713b73aa215217aa245d153" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cfg-if" @@ -220,9 +220,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -230,7 +230,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -338,9 +338,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "der-parser" @@ -414,7 +414,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -443,7 +443,7 @@ dependencies = [ "openssl", "pin-project", "rand", - "rustls", + "rustls-pki-types", "serde", "serde_derive", "serde_json", @@ -459,9 +459,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fnv" @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06fddc2749e0528d2813f95e050e87e52c8cbbae56223b9babf73b3e53b0cc6" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -779,7 +779,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -823,7 +823,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -852,18 +852,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -930,9 +930,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", @@ -957,15 +957,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" dependencies = [ "ring", "rustls-pki-types", @@ -1012,29 +1012,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -1099,9 +1099,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.58" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -1134,29 +1134,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -1175,9 +1175,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -1223,7 +1223,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1366,7 +1366,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -1388,7 +1388,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1427,7 +1427,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1445,7 +1445,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1465,17 +1465,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1486,9 +1487,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -1498,9 +1499,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -1510,9 +1511,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -1522,9 +1529,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -1534,9 +1541,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -1546,9 +1553,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -1558,9 +1565,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "x509-parser" From 70d40ad3c5c8c0014f0e81a7419923acede34908 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 28 Apr 2024 18:12:06 +0500 Subject: [PATCH 116/129] Make min version pass. Use rustls_pki_types dev dep --- Cargo.lock | 1062 ++++++++++++++++++++++++------------------- Cargo.toml | 1 + tests/tls/rustls.rs | 2 +- 3 files changed, 598 insertions(+), 467 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b192f33..6ec49b8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ "gimli", ] [[package]] name = "adler" -version = "1.0.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "6aa100a6f6f525226719f8de3f70076be4f4191801ebd92621450d1c51e9053d" [[package]] name = "android-tzdata" @@ -34,9 +34,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "bff2cf94a3dbe2d57cbd56485e1bd7436455058034d6c2d47be51d4e5e4bc6ab" dependencies = [ "anstyle", "anstyle-parse", @@ -48,49 +48,49 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "asn1-rs" -version = "0.5.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +checksum = "e577111f9ca51289da894bcb4b17047737218c2e4477ea2fc36cd3922172062f" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", "displaydoc", "nom", - "num-traits", + "num-traits 0.2.14", "rusticata-macros", "thiserror", "time", @@ -98,13 +98,13 @@ dependencies = [ [[package]] name = "asn1-rs-derive" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +checksum = "6b9002415e8baa0177a3ae0946fb62ca6e9e470755717409134e44d8e0ae2cad" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 1.0.91", "synstructure", ] @@ -116,57 +116,56 @@ checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 1.0.91", ] [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 1.0.91", ] [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.46", ] [[package]] name = "autocfg" -version = "1.2.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "88fb5a785d6b44fd9d6700935608639af1b8356de1e55d5f7c2740f4faa15d82" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", @@ -175,42 +174,48 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "f5cde24d1b2e2216a726368b2363a273739c91f4e3eb4e0dd12d672d396ad989" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813" [[package]] name = "block-buffer" -version = "0.10.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" [[package]] name = "bytes" -version = "1.6.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72" [[package]] name = "cc" -version = "1.0.95" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" + +[[package]] +name = "cfg-if" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" [[package]] name = "cfg-if" @@ -220,45 +225,55 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", - "num-traits", + "num-traits 0.2.14", "serde", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.0", ] [[package]] name = "clap" -version = "4.5.4" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "codespan-reporting" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6ce42b8998a383572e0a802d859b1f00c79b7b7474e62fff88ee5c2845d9c13" +dependencies = [ + "termcolor", + "unicode-width", +] [[package]] name = "colorchoice" @@ -268,44 +283,93 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.7.0", "libc", ] [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" dependencies = [ "generic-array", - "typenum", +] + +[[package]] +name = "cxx" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c53d75fe543215ca091d792e13351dcb940842dd2829b2a2dd43ab4bd1a015" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb7ed9eb5b6ed9942747aea961a4303b7a56c54f582ea8304cdae391d29d274" +dependencies = [ + "cc", + "codespan-reporting", + "lazy_static", + "proc-macro2", + "quote", + "scratch", + "syn 1.0.91", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca21461be76a23df4f63a2107a0bb406ef41548e635ff7edcbd1ab5a6bb997e2" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8da0a2c0697647b5824844a5d2dedcd97a2d7b75e6e4d0b8dd183e4081e1cf" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.91", ] [[package]] name = "darling" -version = "0.14.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "f1a5d2e8b5a94b2261efb20e99a01255b9c5293797d69bbf04600567b2f9b8d7" dependencies = [ "darling_core", "darling_macro", @@ -313,58 +377,49 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "8f1c7d56716be82d9c6adb967cfe700955179ea88806e898483dad6987330a54" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 1.0.109", + "strsim", + "syn 1.0.91", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "64dd7e5a75a00cb6799ae9fbbfc3bba0134def6579a9e27564e72c839c837bed" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 1.0.91", ] [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" [[package]] name = "der-parser" -version = "8.2.0" +version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" dependencies = [ "asn1-rs", "displaydoc", "nom", "num-bigint", - "num-traits", + "num-traits 0.2.14", "rusticata-macros", ] -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - [[package]] name = "derive_builder" version = "0.12.0" @@ -383,7 +438,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn 1.0.91", ] [[package]] @@ -393,14 +448,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ "derive_builder_core", - "syn 1.0.109", + "syn 1.0.91", ] [[package]] name = "digest" -version = "0.10.7" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" dependencies = [ "block-buffer", "crypto-common", @@ -408,24 +463,20 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "278ef1934318d524612205f69df005eea30ec10edf7913e500b5a527fce55bc0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 1.0.91", ] [[package]] -name = "errno" -version = "0.3.8" +name = "dtoa" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] +checksum = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" [[package]] name = "faktory" @@ -442,7 +493,7 @@ dependencies = [ "oid-registry", "openssl", "pin-project", - "rand", + "rand 0.8.0", "rustls-pki-types", "serde", "serde_derive", @@ -457,12 +508,6 @@ dependencies = [ "x509-parser", ] -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - [[package]] name = "fnv" version = "1.0.7" @@ -471,39 +516,46 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +checksum = "a21b40436003b2a1e22483c5ed6c3d25e755b6b3120f601cc22aa57e25dc9065" dependencies = [ "foreign-types-shared", ] [[package]] name = "foreign-types-shared" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +checksum = "baa1839fc3c5487b5e129ea4f774e3fd84e6c4607127315521bc014a722ebc9e" [[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "fuchsia-zircon" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "3b5365afd01fdf916e775a224e844f80b3b9710d0f4f00903e219e859474d7ae" dependencies = [ - "percent-encoding", + "bitflags 1.0.0", + "fuchsia-zircon-sys", ] +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069def9a0e5feb7e9120635f6ebad24d853a6affbb077fec84d0888316cf9ae6" + [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "30f0ab78f035d7ed5d52689f4b05a56c15ad80097f1d860e644bdc9dba3831f2" [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", "version_check", @@ -511,32 +563,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi", ] [[package]] name = "gimli" -version = "0.28.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" [[package]] name = "hostname" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "01b1af8d6d068ba9de1c39c6ff0d879aed20f74873d4d3929a4535000bb07886" dependencies = [ "libc", "match_cfg", @@ -545,25 +591,26 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" dependencies = [ "android_system_properties", - "core-foundation-sys", + "core-foundation-sys 0.8.3", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" dependencies = [ - "cc", + "cxx", + "cxx-build", ] [[package]] @@ -574,25 +621,32 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ + "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "itoa" -version = "1.0.11" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "91fd9dc2c587067de817fec4ad355e3818c3d893a78cab32a0a474c7a15bb8d5" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -605,21 +659,27 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] -name = "linux-raw-sys" -version = "0.4.13" +name = "link-cplusplus" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "4dfb9f65d9966f6ca6522043978030b564f3291af987fbf1dd55b6a064ba1b36" +dependencies = [ + "cc", +] [[package]] name = "log" -version = "0.4.21" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" +dependencies = [ + "cfg-if 0.1.2", +] [[package]] name = "match_cfg" @@ -627,43 +687,52 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +[[package]] +name = "matches" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e" + [[package]] name = "memchr" -version = "2.7.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "e01e64d9017d18e7fc09d8e4fe0e28ff6931019e979fb8019319db7ca827f8a6" +dependencies = [ + "libc", +] [[package]] name = "minimal-lexical" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.11" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" dependencies = [ "lazy_static", "libc", @@ -679,68 +748,75 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.3" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" dependencies = [ "memchr", "minimal-lexical", + "version_check", ] [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" dependencies = [ "autocfg", "num-integer", - "num-traits", + "num-traits 0.2.14", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "num-traits", + "autocfg", + "num-traits 0.2.14", ] [[package]] name = "num-traits" -version = "0.2.18" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "51eab148f171aefad295f8cece636fc488b9b392ef544da31ea4b8ef6b9e9c39" + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" dependencies = [ - "hermit-abi", "libc", ] [[package]] -name = "object" -version = "0.32.2" +name = "num_threads" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52" dependencies = [ - "memchr", + "libc", ] +[[package]] +name = "object" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" + [[package]] name = "oid-registry" version = "0.6.1" @@ -752,18 +828,18 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" dependencies = [ - "bitflags 2.5.0", - "cfg-if", + "bitflags 2.2.1", + "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", @@ -773,26 +849,26 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 1.0.91", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "756d49c8424483a3df3b5d735112b4da22109ced9a8294f1f5cdf80fb3810919" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" dependencies = [ "cc", "libc", @@ -802,88 +878,93 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "ba4f28a6faf4ffea762ba8f4baef48c61a6db348647c73095034041fc79dd954" [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.46", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" [[package]] name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "powerfmt" -version = "0.2.0" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "rand" -version = "0.8.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "9d5f78082e6a6d042862611e9640cf20776185fee506cf6cf67e93c6225cee31" +dependencies = [ + "fuchsia-zircon", + "libc", +] + +[[package]] +name = "rand" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76330fb486679b4ace3670f117bbc9e16204005c4bde9c4bd372f45bed34f12" dependencies = [ "libc", "rand_chacha", "rand_core", + "rand_hc", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", "rand_core", @@ -891,61 +972,71 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a48131ab10dbeb17202bd1dcb9c9798963a58a50c9ec31640f237358832094" + +[[package]] +name = "remove_dir_all" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc5b3ce5d5ea144bb04ebd093a9e14e9765bcfec866aecda9b6dec43b3d1e24" +dependencies = [ + "winapi", +] + [[package]] name = "ring" -version = "0.17.8" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "fb9d44f9bf6b635117787f72416783eb7e4227aaf255e5ce739563d817176a7e" dependencies = [ "cc", - "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" [[package]] name = "rusticata-macros" -version = "4.1.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +checksum = "65c52377bb2288aa522a0c8208947fada1e0c76397f108cc08f57efe6077b50d" dependencies = [ "nom", ] -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags 2.5.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "rustls" -version = "0.22.4" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "5bc238b76c51bbc449c55ffbc39d03772a057cc8cf783c49d4af4c2537b74a8b" dependencies = [ "log", "ring", @@ -957,116 +1048,117 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.5.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b" [[package]] name = "rustls-webpki" -version = "0.102.3" +version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] -[[package]] -name = "ryu" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" - [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" dependencies = [ - "windows-sys 0.52.0", + "lazy_static", + "winapi", ] +[[package]] +name = "scratch" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69" + [[package]] name = "security-framework" -version = "2.10.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "97bbedbe81904398b6ebb054b3e912f99d55807125790f3198ac990d98def5b0" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.0.0", "core-foundation", - "core-foundation-sys", - "libc", + "core-foundation-sys 0.7.0", "security-framework-sys", ] [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.7.0", "libc", ] [[package]] name = "serde" -version = "1.0.199" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.46", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "e9b1ec939469a124b27e208106550c38358ed4334d2b1b5b3825bc1ee37d946a" dependencies = [ - "itoa", - "ryu", + "dtoa", + "itoa 0.3.0", + "num-traits 0.1.32", "serde", ] [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "spin" -version = "0.9.8" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" [[package]] name = "strsim" @@ -1074,12 +1166,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "subtle" version = "2.5.0" @@ -1088,20 +1174,20 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "1.0.109" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "unicode-xid 0.2.0", ] [[package]] name = "syn" -version = "2.0.60" +version = "2.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" dependencies = [ "proc-macro2", "quote", @@ -1110,99 +1196,81 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.12.6" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "affc27d5f1764f7487bafeb41e380664790716e38ba45d8487bddcc53e79f0f6" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "unicode-xid", + "syn 1.0.91", + "unicode-xid 0.1.0", ] [[package]] name = "tempfile" -version = "3.10.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "47776f63b85777d984a50ce49d6b9e58826b6a3766a449fc95bc66cd5663c15b" dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", + "libc", + "rand 0.4.1", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a52c023823933499250b43960b272e25336c6e2ab8684672edc34489f049ccdd" +dependencies = [ + "wincolor", ] [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 1.0.91", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", + "itoa 1.0.1", + "libc", + "num_threads", "time-macros", ] -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - [[package]] name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" [[package]] name = "tokio" -version = "1.37.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -1212,7 +1280,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1223,7 +1291,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.46", ] [[package]] @@ -1249,9 +1317,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "e4cdeb73537e63f98adcd73138af75e3f368ccaecffaa29d7eb61b9f5a440457" dependencies = [ "futures-core", "pin-project-lite", @@ -1260,9 +1328,9 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" dependencies = [ "async-stream", "bytes", @@ -1273,36 +1341,48 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "2560b941fdb9ea38301b9b708504d612fcdf9c91a8c31d82219bd74cb07d304d" +dependencies = [ + "matches", +] [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] +checksum = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" + +[[package]] +name = "unicode-width" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc85732b6d55a0d520aaf765536a188d9d993770c28633422f85bb646da61335" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "untrusted" @@ -1312,12 +1392,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "77ddaf52e65c6b81c56b7e957c0b1970f7937f21c5c6774c4e56fcb4e20b48c6" dependencies = [ - "form_urlencoded", "idna", + "matches", "percent-encoding", ] @@ -1329,15 +1409,15 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "vcpkg" -version = "0.2.15" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "45d3d553fd9413fffe7147a20171d640eda0ad4c070acd7d0c885a21bcd2e8b7" [[package]] name = "wasi" @@ -1347,34 +1427,34 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", + "lazy_static", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 1.0.91", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1382,28 +1462,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 1.0.91", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "winapi" -version = "0.3.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "b3ad91d846a4a5342c1fb7008d26124ee6cf94a3953751618577295373b32117" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -1411,23 +1491,32 @@ dependencies = [ [[package]] name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "a16a8e2ebfc883e2b1771c6482b1fb3c6831eab289ba391619a2d93a7356220f" [[package]] name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "8ca29cb03c8ceaf20f8224a18a530938305e9872b1478ea24ff44b4f503a1d1d" [[package]] -name = "windows-core" -version = "0.52.0" +name = "wincolor" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "b9dc3aa9dcda98b5a16150c54619c1ead22e3d3a5d458778ae914be760aa981a" dependencies = [ - "windows-targets 0.52.5", + "winapi", +] + +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -1436,138 +1525,179 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.48.0", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-targets" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows-targets 0.52.5", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" +name = "windows_i686_gnu" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "x509-parser" @@ -1588,6 +1718,6 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.7.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml index a2720c70..d4987f00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ tokio-rustls = { version = "0.25.0", optional = true } url = "2" [dev-dependencies] +rustls-pki-types = "1.0.1" tokio = { version = "1.35.1", features = ["rt", "macros"] } tokio-test = "0.4.3" x509-parser = "0.15.1" diff --git a/tests/tls/rustls.rs b/tests/tls/rustls.rs index 5d6e627b..9bdc4301 100644 --- a/tests/tls/rustls.rs +++ b/tests/tls/rustls.rs @@ -119,10 +119,10 @@ mod fixtures { use std::fs; use std::path::PathBuf; + use rustls_pki_types::{CertificateDer, ServerName, UnixTime}; use tokio_rustls::rustls::client::danger::{ HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, }; - use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime}; use tokio_rustls::rustls::DigitallySignedStruct; use tokio_rustls::rustls::Error as RustlsError; use tokio_rustls::rustls::SignatureScheme; From 3e592b02a8a6036c0e6c05b0a97fe945ed4b89d0 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 29 Apr 2024 07:50:44 +0500 Subject: [PATCH 117/129] Rename Client::info to Client::current_info --- src/proto/client/mod.rs | 2 +- src/proto/single/resp.rs | 2 +- tests/real/community.rs | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 985485a0..64712af9 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -375,7 +375,7 @@ where /// Retrieve [information](crate::ServerState) about the running server. /// /// The returned value is the result of running the `INFO` command on the server. - pub async fn info(&mut self) -> Result { + pub async fn current_info(&mut self) -> Result { self.issue(&Info) .await? .read_json() diff --git a/src/proto/single/resp.rs b/src/proto/single/resp.rs index d4ec2606..5609e670 100644 --- a/src/proto/single/resp.rs +++ b/src/proto/single/resp.rs @@ -185,7 +185,7 @@ pub struct FaktoryServerProcessStats { /// use faktory::Client; /// /// let mut client = Client::connect(None).await.unwrap(); -/// let _server_state = client.info().await.unwrap(); +/// let _server_state = client.current_info().await.unwrap(); /// # }); /// ``` #[derive(Serialize, Deserialize, Debug, Clone)] diff --git a/tests/real/community.rs b/tests/real/community.rs index 2cffd9ce..93ee7af4 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -87,7 +87,7 @@ async fn server_state() { let mut client = Client::connect(None).await.unwrap(); // examine server state before pushing anything - let server_state = client.info().await.unwrap(); + let server_state = client.current_info().await.unwrap(); assert!(server_state.faktory.queues.get(local).is_none()); // the following two assertions are not super-helpful but // there is not much info we can make meaningful assetions on anyhow @@ -107,7 +107,7 @@ async fn server_state() { .unwrap(); // we only pushed 1 job on this queue - let server_state = client.info().await.unwrap(); + let server_state = client.current_info().await.unwrap(); assert_eq!(*server_state.faktory.queues.get(local).unwrap(), 1); assert!(server_state.faktory.total_enqueued >= 1); // at least 1 job from this test assert!(server_state.faktory.total_queues >= 1); // at least 1 qeueu from this test @@ -123,14 +123,14 @@ async fn server_state() { // But generally, we are performing a clean-up by consuming the jobs from the local queue/ // and then deleting the queue programmatically, so there is normally no need to prune docker // volumes to perform the next test run. Also note that on CI we are always starting a-fresh. - let server_state = client.info().await.unwrap(); + let server_state = client.current_info().await.unwrap(); assert_eq!(*server_state.faktory.queues.get(local).unwrap(), 0); assert!(server_state.faktory.total_processed >= 1); // at least 1 job from this test client.queue_remove(&[local]).await.unwrap(); assert!(client - .info() + .current_info() .await .unwrap() .faktory @@ -304,7 +304,7 @@ async fn queue_control_actions() { assert!(rx.try_recv().is_ok()); // let's inspect the sever state - let server_state = client.info().await.unwrap(); + let server_state = client.current_info().await.unwrap(); let queues = &server_state.faktory.queues; assert_eq!(*queues.get(local_1).unwrap(), 1); // 1 job remaining assert_eq!(*queues.get(local_2).unwrap(), 1); // also 1 job remaining @@ -319,7 +319,7 @@ async fn queue_control_actions() { assert!(!rx.try_recv().is_ok()); // let's inspect the sever state again - let server_state = client.info().await.unwrap(); + let server_state = client.current_info().await.unwrap(); let queues = &server_state.faktory.queues; // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); @@ -386,7 +386,7 @@ async fn queue_control_actions_wildcard() { assert!(rx.try_recv().is_ok()); // let's inspect the sever state - let server_state = client.info().await.unwrap(); + let server_state = client.current_info().await.unwrap(); let queues = &server_state.faktory.queues; assert_eq!(*queues.get(local_1).unwrap(), 1); // 1 job remaining assert_eq!(*queues.get(local_2).unwrap(), 1); // also 1 job remaining @@ -401,7 +401,7 @@ async fn queue_control_actions_wildcard() { assert!(!rx.try_recv().is_ok()); // let's inspect the sever state again - let server_state = client.info().await.unwrap(); + let server_state = client.current_info().await.unwrap(); let queues = &server_state.faktory.queues; // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); From 3c262dff2f97ebc907d78ef0447424f9f981d7d5 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 29 Apr 2024 08:40:16 +0500 Subject: [PATCH 118/129] Annotate ServerState as non-exhaustive and DataSnapshot::tasks as deprecated --- src/lib.rs | 3 +-- src/proto/client/mod.rs | 4 ++-- src/proto/mod.rs | 4 +--- src/proto/single/resp.rs | 18 +++++++++++------- tests/real/community.rs | 25 ++++++++++++------------- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ac33907d..f8f97828 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,8 +76,7 @@ mod worker; pub use crate::error::Error; pub use crate::proto::{ - Client, FaktoryServerProcessStats, FaktoryServiceStats, Job, JobBuilder, JobId, Reconnect, - ServerState, WorkerId, + Client, DataSnapshot, FaktoryState, Job, JobBuilder, JobId, Reconnect, ServerSnapshot, WorkerId, }; pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 64712af9..8dbf8e64 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -372,10 +372,10 @@ where Ok((jobs_count - errors.len(), Some(errors))) } - /// Retrieve [information](crate::ServerState) about the running server. + /// Retrieve [information](crate::ServerSnapshot) about the running server. /// /// The returned value is the result of running the `INFO` command on the server. - pub async fn current_info(&mut self) -> Result { + pub async fn current_info(&mut self) -> Result { self.issue(&Info) .await? .read_json() diff --git a/src/proto/mod.rs b/src/proto/mod.rs index a92788d6..63f479cd 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -9,9 +9,7 @@ pub(crate) use client::{ClientOptions, HeartbeatStatus, EXPECTED_PROTOCOL_VERSIO mod single; -pub use single::{ - FaktoryServerProcessStats, FaktoryServiceStats, Job, JobBuilder, JobId, ServerState, WorkerId, -}; +pub use single::{DataSnapshot, FaktoryState, Job, JobBuilder, JobId, ServerSnapshot, WorkerId}; pub(crate) use single::{Ack, Fail, Info, Push, PushBulk, QueueAction, QueueControl}; diff --git a/src/proto/single/resp.rs b/src/proto/single/resp.rs index 5609e670..4231c95d 100644 --- a/src/proto/single/resp.rs +++ b/src/proto/single/resp.rs @@ -125,7 +125,8 @@ pub async fn read_ok(r: R) -> Result<(), Error> { /// Faktory service stats. #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct FaktoryServiceStats { +#[non_exhaustive] +pub struct DataSnapshot { /// Total number of job failures. pub total_failures: u64, @@ -145,21 +146,23 @@ pub struct FaktoryServiceStats { /// registered in the Faktory service. pub queues: HashMap, - /// Faktory's task runner stats. + /// ***Deprecated***. Faktory's task runner stats. /// /// Note that this is exposed as a "generic" `serde_json::Value` since this info /// belongs to deep implementation details of the Faktory service. + #[deprecated] pub tasks: serde_json::Value, } /// Faktory's server process stats. #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct FaktoryServerProcessStats { +pub struct ServerSnapshot { /// Faktory's description (e.g. "Faktory"). pub description: String, /// Faktory's version as semver (e.g. "1.8.0"). - pub faktory_version: String, + #[serde(rename = "faktory_version")] + pub version: String, /// Faktory server process uptime in seconds. pub uptime: u64, @@ -189,7 +192,7 @@ pub struct FaktoryServerProcessStats { /// # }); /// ``` #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ServerState { +pub struct FaktoryState { /// Server time. pub now: DateTime, @@ -197,10 +200,11 @@ pub struct ServerState { pub server_utc_time: String, /// Faktory service stats. - pub faktory: FaktoryServiceStats, + #[serde(rename = "faktory")] + pub data: DataSnapshot, /// Faktory's server process stats. - pub server: FaktoryServerProcessStats, + pub server: ServerSnapshot, } // ---------------------------------------------- diff --git a/tests/real/community.rs b/tests/real/community.rs index 93ee7af4..9a1ddfd4 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -88,7 +88,7 @@ async fn server_state() { // examine server state before pushing anything let server_state = client.current_info().await.unwrap(); - assert!(server_state.faktory.queues.get(local).is_none()); + assert!(server_state.data.queues.get(local).is_none()); // the following two assertions are not super-helpful but // there is not much info we can make meaningful assetions on anyhow // (like memusage, server description string, version, etc.) @@ -108,11 +108,10 @@ async fn server_state() { // we only pushed 1 job on this queue let server_state = client.current_info().await.unwrap(); - assert_eq!(*server_state.faktory.queues.get(local).unwrap(), 1); - assert!(server_state.faktory.total_enqueued >= 1); // at least 1 job from this test - assert!(server_state.faktory.total_queues >= 1); // at least 1 qeueu from this test - - // let's know consume that job ... + assert_eq!(*server_state.data.queues.get(local).unwrap(), 1); + assert!(server_state.data.total_enqueued >= 1); // at least 1 job from this test + assert!(server_state.data.total_queues >= 1); // at least 1 qeueu from this test + // let's know consume that job ... assert!(w.run_one(0, &[local]).await.unwrap()); // ... and verify the queue has got 0 pending jobs @@ -124,8 +123,8 @@ async fn server_state() { // and then deleting the queue programmatically, so there is normally no need to prune docker // volumes to perform the next test run. Also note that on CI we are always starting a-fresh. let server_state = client.current_info().await.unwrap(); - assert_eq!(*server_state.faktory.queues.get(local).unwrap(), 0); - assert!(server_state.faktory.total_processed >= 1); // at least 1 job from this test + assert_eq!(*server_state.data.queues.get(local).unwrap(), 0); + assert!(server_state.data.total_processed >= 1); // at least 1 job from this test client.queue_remove(&[local]).await.unwrap(); @@ -133,7 +132,7 @@ async fn server_state() { .current_info() .await .unwrap() - .faktory + .data .queues .get(local) .is_none()); @@ -305,7 +304,7 @@ async fn queue_control_actions() { // let's inspect the sever state let server_state = client.current_info().await.unwrap(); - let queues = &server_state.faktory.queues; + let queues = &server_state.data.queues; assert_eq!(*queues.get(local_1).unwrap(), 1); // 1 job remaining assert_eq!(*queues.get(local_2).unwrap(), 1); // also 1 job remaining @@ -320,7 +319,7 @@ async fn queue_control_actions() { // let's inspect the sever state again let server_state = client.current_info().await.unwrap(); - let queues = &server_state.faktory.queues; + let queues = &server_state.data.queues; // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); assert!(queues.get(local_2).is_none()); @@ -387,7 +386,7 @@ async fn queue_control_actions_wildcard() { // let's inspect the sever state let server_state = client.current_info().await.unwrap(); - let queues = &server_state.faktory.queues; + let queues = &server_state.data.queues; assert_eq!(*queues.get(local_1).unwrap(), 1); // 1 job remaining assert_eq!(*queues.get(local_2).unwrap(), 1); // also 1 job remaining @@ -402,7 +401,7 @@ async fn queue_control_actions_wildcard() { // let's inspect the sever state again let server_state = client.current_info().await.unwrap(); - let queues = &server_state.faktory.queues; + let queues = &server_state.data.queues; // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); assert!(queues.get(local_2).is_none()); From ccd57076c3aeb8ac7eec00d3f8bf974d72a453a3 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 12 May 2024 21:18:34 +0500 Subject: [PATCH 119/129] Resrtore Duration::seconds in tests::real::enterprise --- tests/real/enterprise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index be261d4f..94fe13b8 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -51,7 +51,7 @@ async fn ent_expiring_job() { // prepare an expiring job: let job_ttl_secs: u64 = 3; - let ttl = chrono::Duration::try_seconds(job_ttl_secs as i64).unwrap(); + let ttl = chrono::Duration::seconds(job_ttl_secs as i64); let job1 = JobBuilder::new("AnExpiringJob") .args(vec!["ISBN-13:9781718501850"]) .queue(local) From 7653dfdfba228fcd0a49a3628c4b8a26018c4415 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 12 May 2024 21:22:07 +0500 Subject: [PATCH 120/129] Resrtore Duration::seconds in proto::single::ent --- src/proto/single/ent/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proto/single/ent/mod.rs b/src/proto/single/ent/mod.rs index 7961f8b0..e6f4f63e 100644 --- a/src/proto/single/ent/mod.rs +++ b/src/proto/single/ent/mod.rs @@ -100,7 +100,7 @@ mod test { #[test] fn test_expiration_feature_for_enterprise_faktory() { - let five_min = chrono::Duration::try_seconds(299).unwrap(); + let five_min = chrono::Duration::seconds(299); let exp_at = Utc::now() + five_min; let job0 = half_stuff().expires_at(exp_at).build(); let stored = job0.custom.get("expires_at").unwrap(); @@ -126,8 +126,8 @@ mod test { #[test] fn test_same_purpose_setters_applied_simultaneously() { - let expires_at0 = Utc::now() + chrono::Duration::try_seconds(300).unwrap(); - let expires_at1 = Utc::now() + chrono::Duration::try_seconds(300).unwrap(); + let expires_at0 = Utc::now() + chrono::Duration::seconds(300); + let expires_at1 = Utc::now() + chrono::Duration::seconds(300); let job = half_stuff() .unique_for(59) .add_to_custom_data("unique_for", 599) From 0a6d1b50a08bfd142639d7882ed6182eb21f00c3 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 12 May 2024 21:26:14 +0500 Subject: [PATCH 121/129] Checkout lockifile to main --- Cargo.lock | 1070 +++++++++++++++++++++++++++++----------------------- 1 file changed, 600 insertions(+), 470 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2405395..8505c38b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ "gimli", ] [[package]] name = "adler" -version = "1.0.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "6aa100a6f6f525226719f8de3f70076be4f4191801ebd92621450d1c51e9053d" [[package]] name = "android-tzdata" @@ -34,64 +34,63 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "bff2cf94a3dbe2d57cbd56485e1bd7436455058034d6c2d47be51d4e5e4bc6ab" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "0238ca56c96dfa37bdf7c373c8886dd591322500aceeeccdb2216fe06dc2f796" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "asn1-rs" -version = "0.5.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +checksum = "e577111f9ca51289da894bcb4b17047737218c2e4477ea2fc36cd3922172062f" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", "displaydoc", "nom", - "num-traits", + "num-traits 0.2.14", "rusticata-macros", "thiserror", "time", @@ -99,13 +98,13 @@ dependencies = [ [[package]] name = "asn1-rs-derive" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +checksum = "6b9002415e8baa0177a3ae0946fb62ca6e9e470755717409134e44d8e0ae2cad" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 1.0.91", "synstructure", ] @@ -117,57 +116,56 @@ checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 1.0.91", ] [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" dependencies = [ "async-stream-impl", "futures-core", - "pin-project-lite", ] [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 1.0.91", ] [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.46", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "88fb5a785d6b44fd9d6700935608639af1b8356de1e55d5f7c2740f4faa15d82" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", @@ -176,36 +174,48 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.5.0" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5cde24d1b2e2216a726368b2363a273739c91f4e3eb4e0dd12d672d396ad989" + +[[package]] +name = "bitflags" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813" [[package]] name = "block-buffer" -version = "0.10.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" [[package]] name = "bytes" -version = "1.6.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72" [[package]] name = "cc" -version = "1.0.97" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" + +[[package]] +name = "cfg-if" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" [[package]] name = "cfg-if" @@ -215,90 +225,149 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" dependencies = [ "android-tzdata", "iana-time-zone", - "num-traits", + "num-traits 0.2.14", "serde", - "windows-targets 0.52.5", + "windows-targets 0.52.0", ] [[package]] name = "clap" -version = "4.5.4" +version = "4.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "codespan-reporting" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "c6ce42b8998a383572e0a802d859b1f00c79b7b7474e62fff88ee5c2845d9c13" +dependencies = [ + "termcolor", + "unicode-width", +] [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "core-foundation" -version = "0.9.4" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.7.0", "libc", ] [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" dependencies = [ "generic-array", - "typenum", +] + +[[package]] +name = "cxx" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c53d75fe543215ca091d792e13351dcb940842dd2829b2a2dd43ab4bd1a015" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb7ed9eb5b6ed9942747aea961a4303b7a56c54f582ea8304cdae391d29d274" +dependencies = [ + "cc", + "codespan-reporting", + "lazy_static", + "proc-macro2", + "quote", + "scratch", + "syn 1.0.91", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca21461be76a23df4f63a2107a0bb406ef41548e635ff7edcbd1ab5a6bb997e2" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8da0a2c0697647b5824844a5d2dedcd97a2d7b75e6e4d0b8dd183e4081e1cf" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.91", ] [[package]] name = "darling" -version = "0.14.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +checksum = "f1a5d2e8b5a94b2261efb20e99a01255b9c5293797d69bbf04600567b2f9b8d7" dependencies = [ "darling_core", "darling_macro", @@ -306,58 +375,49 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.14.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +checksum = "8f1c7d56716be82d9c6adb967cfe700955179ea88806e898483dad6987330a54" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 1.0.109", + "strsim", + "syn 1.0.91", ] [[package]] name = "darling_macro" -version = "0.14.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +checksum = "64dd7e5a75a00cb6799ae9fbbfc3bba0134def6579a9e27564e72c839c837bed" dependencies = [ "darling_core", "quote", - "syn 1.0.109", + "syn 1.0.91", ] [[package]] name = "data-encoding" -version = "2.6.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69" [[package]] name = "der-parser" -version = "8.2.0" +version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1" dependencies = [ "asn1-rs", "displaydoc", "nom", "num-bigint", - "num-traits", + "num-traits 0.2.14", "rusticata-macros", ] -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - [[package]] name = "derive_builder" version = "0.12.0" @@ -376,7 +436,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 1.0.109", + "syn 1.0.91", ] [[package]] @@ -386,14 +446,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e" dependencies = [ "derive_builder_core", - "syn 1.0.109", + "syn 1.0.91", ] [[package]] name = "digest" -version = "0.10.7" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" dependencies = [ "block-buffer", "crypto-common", @@ -401,24 +461,20 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "278ef1934318d524612205f69df005eea30ec10edf7913e500b5a527fce55bc0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 1.0.91", ] [[package]] -name = "errno" -version = "0.3.9" +name = "dtoa" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] +checksum = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" [[package]] name = "faktory" @@ -435,7 +491,7 @@ dependencies = [ "oid-registry", "openssl", "pin-project", - "rand", + "rand 0.8.0", "rustls-pki-types", "serde", "serde_derive", @@ -450,12 +506,6 @@ dependencies = [ "x509-parser", ] -[[package]] -name = "fastrand" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" - [[package]] name = "fnv" version = "1.0.7" @@ -464,39 +514,46 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +checksum = "a21b40436003b2a1e22483c5ed6c3d25e755b6b3120f601cc22aa57e25dc9065" dependencies = [ "foreign-types-shared", ] [[package]] name = "foreign-types-shared" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +checksum = "baa1839fc3c5487b5e129ea4f774e3fd84e6c4607127315521bc014a722ebc9e" [[package]] -name = "form_urlencoded" -version = "1.2.1" +name = "fuchsia-zircon" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "3b5365afd01fdf916e775a224e844f80b3b9710d0f4f00903e219e859474d7ae" dependencies = [ - "percent-encoding", + "bitflags 1.0.0", + "fuchsia-zircon-sys", ] +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069def9a0e5feb7e9120635f6ebad24d853a6affbb077fec84d0888316cf9ae6" + [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "30f0ab78f035d7ed5d52689f4b05a56c15ad80097f1d860e644bdc9dba3831f2" [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" dependencies = [ "typenum", "version_check", @@ -504,32 +561,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi", ] [[package]] name = "gimli" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" - -[[package]] -name = "hermit-abi" -version = "0.3.9" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" [[package]] name = "hostname" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "01b1af8d6d068ba9de1c39c6ff0d879aed20f74873d4d3929a4535000bb07886" dependencies = [ "libc", "match_cfg", @@ -538,25 +589,26 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" dependencies = [ "android_system_properties", - "core-foundation-sys", + "core-foundation-sys 0.8.3", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" dependencies = [ - "cc", + "cxx", + "cxx-build", ] [[package]] @@ -567,31 +619,32 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ + "matches", "unicode-bidi", "unicode-normalization", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.0" +name = "itoa" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "91fd9dc2c587067de817fec4ad355e3818c3d893a78cab32a0a474c7a15bb8d5" [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" dependencies = [ "wasm-bindgen", ] @@ -604,21 +657,27 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] -name = "linux-raw-sys" -version = "0.4.13" +name = "link-cplusplus" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "4dfb9f65d9966f6ca6522043978030b564f3291af987fbf1dd55b6a064ba1b36" +dependencies = [ + "cc", +] [[package]] name = "log" -version = "0.4.21" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" +dependencies = [ + "cfg-if 0.1.2", +] [[package]] name = "match_cfg" @@ -626,43 +685,52 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +[[package]] +name = "matches" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15305656809ce5a4805b1ff2946892810992197ce1270ff79baded852187942e" + [[package]] name = "memchr" -version = "2.7.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "e01e64d9017d18e7fc09d8e4fe0e28ff6931019e979fb8019319db7ca827f8a6" +dependencies = [ + "libc", +] [[package]] name = "minimal-lexical" -version = "0.2.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "6595bb28ed34f43c3fe088e48f6cfb2e033cab45f25a5384d5fdf564fbc8c4b2" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.11" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" dependencies = [ "lazy_static", "libc", @@ -678,67 +746,75 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.3" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" dependencies = [ "memchr", "minimal-lexical", + "version_check", ] [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "74e768dff5fb39a41b3bcd30bb25cf989706c90d028d1ad71971987aa309d535" dependencies = [ + "autocfg", "num-integer", - "num-traits", + "num-traits 0.2.14", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "num-traits", + "autocfg", + "num-traits 0.2.14", ] [[package]] name = "num-traits" -version = "0.2.19" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51eab148f171aefad295f8cece636fc488b9b392ef544da31ea4b8ef6b9e9c39" + +[[package]] +name = "num-traits" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" dependencies = [ - "hermit-abi", "libc", ] [[package]] -name = "object" -version = "0.32.2" +name = "num_threads" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52" dependencies = [ - "memchr", + "libc", ] +[[package]] +name = "object" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" + [[package]] name = "oid-registry" version = "0.6.1" @@ -750,18 +826,18 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" dependencies = [ - "bitflags", - "cfg-if", + "bitflags 2.2.1", + "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", @@ -771,26 +847,26 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.1" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 1.0.91", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "756d49c8424483a3df3b5d735112b4da22109ced9a8294f1f5cdf80fb3810919" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" dependencies = [ "cc", "libc", @@ -800,88 +876,93 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "ba4f28a6faf4ffea762ba8f4baef48c61a6db348647c73095034041fc79dd954" [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.46", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c" [[package]] name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "powerfmt" -version = "0.2.0" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "rand" -version = "0.8.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "9d5f78082e6a6d042862611e9640cf20776185fee506cf6cf67e93c6225cee31" +dependencies = [ + "fuchsia-zircon", + "libc", +] + +[[package]] +name = "rand" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76330fb486679b4ace3670f117bbc9e16204005c4bde9c4bd372f45bed34f12" dependencies = [ "libc", "rand_chacha", "rand_core", + "rand_hc", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", "rand_core", @@ -889,61 +970,71 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a48131ab10dbeb17202bd1dcb9c9798963a58a50c9ec31640f237358832094" + +[[package]] +name = "remove_dir_all" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc5b3ce5d5ea144bb04ebd093a9e14e9765bcfec866aecda9b6dec43b3d1e24" +dependencies = [ + "winapi", +] + [[package]] name = "ring" -version = "0.17.8" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "fb9d44f9bf6b635117787f72416783eb7e4227aaf255e5ce739563d817176a7e" dependencies = [ "cc", - "cfg-if", "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" [[package]] name = "rusticata-macros" -version = "4.1.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +checksum = "65c52377bb2288aa522a0c8208947fada1e0c76397f108cc08f57efe6077b50d" dependencies = [ "nom", ] -[[package]] -name = "rustix" -version = "0.38.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - [[package]] name = "rustls" -version = "0.22.4" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "5bc238b76c51bbc449c55ffbc39d03772a057cc8cf783c49d4af4c2537b74a8b" dependencies = [ "log", "ring", @@ -955,116 +1046,117 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b" [[package]] name = "rustls-webpki" -version = "0.102.3" +version = "0.102.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" dependencies = [ - "windows-sys 0.52.0", + "lazy_static", + "winapi", ] +[[package]] +name = "scratch" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69" + [[package]] name = "security-framework" -version = "2.11.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "97bbedbe81904398b6ebb054b3e912f99d55807125790f3198ac990d98def5b0" dependencies = [ - "bitflags", + "bitflags 1.0.0", "core-foundation", - "core-foundation-sys", - "libc", + "core-foundation-sys 0.7.0", "security-framework-sys", ] [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" dependencies = [ - "core-foundation-sys", + "core-foundation-sys 0.7.0", "libc", ] [[package]] name = "serde" -version = "1.0.201" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.46", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "e9b1ec939469a124b27e208106550c38358ed4334d2b1b5b3825bc1ee37d946a" dependencies = [ - "itoa", - "ryu", + "dtoa", + "itoa 0.3.0", + "num-traits 0.1.32", "serde", ] [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest", ] [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "spin" -version = "0.9.8" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" [[package]] name = "strsim" @@ -1072,12 +1164,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "subtle" version = "2.5.0" @@ -1086,20 +1172,20 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "1.0.109" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "unicode-xid 0.2.0", ] [[package]] name = "syn" -version = "2.0.63" +version = "2.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" dependencies = [ "proc-macro2", "quote", @@ -1108,99 +1194,81 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.12.6" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "affc27d5f1764f7487bafeb41e380664790716e38ba45d8487bddcc53e79f0f6" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", - "unicode-xid", + "syn 1.0.91", + "unicode-xid 0.1.0", ] [[package]] name = "tempfile" -version = "3.10.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "47776f63b85777d984a50ce49d6b9e58826b6a3766a449fc95bc66cd5663c15b" dependencies = [ - "cfg-if", - "fastrand", - "rustix", - "windows-sys 0.52.0", + "libc", + "rand 0.4.1", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a52c023823933499250b43960b272e25336c6e2ab8684672edc34489f049ccdd" +dependencies = [ + "wincolor", ] [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 1.0.91", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde", - "time-core", + "itoa 1.0.1", + "libc", + "num_threads", "time-macros", ] -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" [[package]] name = "tokio" -version = "1.37.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -1210,7 +1278,7 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1221,7 +1289,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.46", ] [[package]] @@ -1247,9 +1315,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "e4cdeb73537e63f98adcd73138af75e3f368ccaecffaa29d7eb61b9f5a440457" dependencies = [ "futures-core", "pin-project-lite", @@ -1258,9 +1326,9 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" dependencies = [ "async-stream", "bytes", @@ -1271,36 +1339,48 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "2560b941fdb9ea38301b9b708504d612fcdf9c91a8c31d82219bd74cb07d304d" +dependencies = [ + "matches", +] [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] +checksum = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" + +[[package]] +name = "unicode-width" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc85732b6d55a0d520aaf765536a188d9d993770c28633422f85bb646da61335" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "untrusted" @@ -1310,12 +1390,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "77ddaf52e65c6b81c56b7e957c0b1970f7937f21c5c6774c4e56fcb4e20b48c6" dependencies = [ - "form_urlencoded", "idna", + "matches", "percent-encoding", ] @@ -1327,15 +1407,15 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "vcpkg" -version = "0.2.15" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "45d3d553fd9413fffe7147a20171d640eda0ad4c070acd7d0c885a21bcd2e8b7" [[package]] name = "wasi" @@ -1345,34 +1425,34 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" dependencies = [ "bumpalo", + "lazy_static", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.63", + "syn 1.0.91", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1380,28 +1460,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 1.0.91", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "winapi" -version = "0.3.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "b3ad91d846a4a5342c1fb7008d26124ee6cf94a3953751618577295373b32117" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -1409,23 +1489,32 @@ dependencies = [ [[package]] name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "a16a8e2ebfc883e2b1771c6482b1fb3c6831eab289ba391619a2d93a7356220f" [[package]] name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "8ca29cb03c8ceaf20f8224a18a530938305e9872b1478ea24ff44b4f503a1d1d" [[package]] -name = "windows-core" -version = "0.52.0" +name = "wincolor" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "b9dc3aa9dcda98b5a16150c54619c1ead22e3d3a5d458778ae914be760aa981a" dependencies = [ - "windows-targets 0.52.5", + "winapi", +] + +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets 0.42.2", ] [[package]] @@ -1434,138 +1523,179 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.48.0", ] [[package]] -name = "windows-sys" -version = "0.52.0" +name = "windows-targets" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows-targets 0.52.5", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] -name = "windows_i686_gnullvm" -version = "0.52.5" +name = "windows_i686_gnu" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "x509-parser" @@ -1586,6 +1716,6 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.7.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" From 910d47eddd057b86f100c76241a5cf15a4cbf79d Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 12 May 2024 21:30:19 +0500 Subject: [PATCH 122/129] Refactor Client::may_bid using pattern matching --- src/proto/client/ent.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index 2b54269a..d85900db 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -61,15 +61,13 @@ impl<'a, S: AsyncBufRead + AsyncWrite + Unpin + Send> ReadToken<'a, S> { pub(crate) async fn maybe_bid(self) -> Result, Error> { match single::read_bid(&mut self.0.stream).await { Ok(bid) => Ok(Some(bid)), - Err(err) => match err { - Error::Protocol(error::Protocol::Internal { msg }) => { - if msg.starts_with("No such batch") { - return Ok(None); - } - Err(error::Protocol::Internal { msg }.into()) + Err(Error::Protocol(error::Protocol::Internal { msg })) => { + if msg.starts_with("No such batch") { + return Ok(None); } - another => Err(another), - }, + Err(error::Protocol::Internal { msg }.into()) + } + Err(another) => Err(another), } } } From bd6469994c72e44b62d16887e5855979accf8db7 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 12 May 2024 22:30:46 +0500 Subject: [PATCH 123/129] Do not require ownerwship in Client::set_progess and Client::get_progress --- src/proto/client/ent.rs | 18 ++++++++----- src/proto/client/mod.rs | 2 +- src/proto/mod.rs | 5 +++- src/proto/single/ent/cmd.rs | 43 +++++++++++++++++++------------- src/proto/single/ent/mod.rs | 2 +- src/proto/single/ent/progress.rs | 6 +++++ tests/real/enterprise.rs | 14 +++++------ 7 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index d85900db..b5dcf72b 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -1,20 +1,26 @@ use super::super::batch::{CommitBatch, GetBatchStatus, OpenBatch}; -use super::super::{single, BatchStatus, JobId, Progress, ProgressUpdate, Track}; +use super::super::{single, BatchStatus, JobId, Progress, ProgressUpdate}; use super::{Client, ReadToken}; use crate::ent::{Batch, BatchHandle, BatchId}; use crate::error::{self, Error}; +use crate::proto::FetchProgress; use tokio::io::{AsyncBufRead, AsyncWrite}; impl Client { /// Send information on a job's execution progress to Faktory. - pub async fn set_progress(&mut self, upd: ProgressUpdate) -> Result<(), Error> { - let cmd = Track::Set(upd); - self.issue(&cmd).await?.read_ok().await + pub async fn set_progress

(&mut self, upd: P) -> Result<(), Error> + where + P: AsRef + Sync, + { + self.issue(&upd).await?.read_ok().await } /// Fetch information on a job's execution progress from Faktory. - pub async fn get_progress(&mut self, jid: JobId) -> Result, Error> { - let cmd = Track::Get(jid); + pub async fn get_progress(&mut self, jid: J) -> Result, Error> + where + J: AsRef + Sync, + { + let cmd = FetchProgress::new(jid); self.issue(&cmd).await?.read_json().await } diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 162c9b5d..4b55870f 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -127,7 +127,7 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// .desc("Almost done...".to_owned()) /// .percent(99) /// .build(); -/// cl.set_progress(progress).await?; +/// cl.set_progress(&progress).await?; /// # Ok::<(), faktory::Error>(()) /// }); ///```` diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 63f479cd..7d65ff6e 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -16,7 +16,10 @@ pub(crate) use single::{Ack, Fail, Info, Push, PushBulk, QueueAction, QueueContr pub(crate) mod utils; #[cfg(feature = "ent")] -pub use self::single::ent::{JobState, Progress, ProgressUpdate, ProgressUpdateBuilder, Track}; +pub use self::single::ent::{JobState, Progress, ProgressUpdate, ProgressUpdateBuilder}; + +#[cfg(feature = "ent")] +pub(crate) use self::single::ent::FetchProgress; #[cfg(feature = "ent")] pub use self::single::BatchId; diff --git a/src/proto/single/ent/cmd.rs b/src/proto/single/ent/cmd.rs index 43881e44..8edb5a88 100644 --- a/src/proto/single/ent/cmd.rs +++ b/src/proto/single/ent/cmd.rs @@ -3,27 +3,36 @@ use crate::error::Error; use crate::proto::{single::FaktoryCommand, JobId}; use tokio::io::{AsyncWrite, AsyncWriteExt}; +#[async_trait::async_trait] +impl

FaktoryCommand for P +where + P: AsRef + Sync, +{ + async fn issue(&self, w: &mut W) -> Result<(), Error> { + w.write_all(b"TRACK SET ").await?; + let r = serde_json::to_vec(self.as_ref()).map_err(Error::Serialization)?; + w.write_all(&r).await?; + Ok(w.write_all(b"\r\n").await?) + } +} + #[derive(Debug, Clone)] -pub enum Track { - Set(ProgressUpdate), - Get(JobId), +pub(crate) struct FetchProgress(J); + +impl FetchProgress { + pub fn new(j: J) -> Self { + Self(j) + } } #[async_trait::async_trait] -impl FaktoryCommand for Track { +impl FaktoryCommand for FetchProgress +where + J: AsRef + Sync, +{ async fn issue(&self, w: &mut W) -> Result<(), Error> { - match self { - Self::Set(upd) => { - w.write_all(b"TRACK SET ").await?; - let r = serde_json::to_vec(upd).map_err(Error::Serialization)?; - w.write_all(&r).await?; - Ok(w.write_all(b"\r\n").await?) - } - Self::Get(jid) => { - w.write_all(b"TRACK GET ").await?; - w.write_all(jid.as_bytes()).await?; - Ok(w.write_all(b"\r\n").await?) - } - } + w.write_all(b"TRACK GET ").await?; + w.write_all(self.0.as_ref().as_bytes()).await?; + Ok(w.write_all(b"\r\n").await?) } } diff --git a/src/proto/single/ent/mod.rs b/src/proto/single/ent/mod.rs index e6f4f63e..2a017c49 100644 --- a/src/proto/single/ent/mod.rs +++ b/src/proto/single/ent/mod.rs @@ -5,7 +5,7 @@ mod cmd; mod progress; mod utils; -pub use cmd::Track; +pub(crate) use cmd::FetchProgress; pub use progress::{JobState, Progress, ProgressUpdate, ProgressUpdateBuilder}; impl JobBuilder { diff --git a/src/proto/single/ent/progress.rs b/src/proto/single/ent/progress.rs index 49304a5a..f7ca87d4 100644 --- a/src/proto/single/ent/progress.rs +++ b/src/proto/single/ent/progress.rs @@ -38,6 +38,12 @@ pub struct ProgressUpdate { pub reserve_until: Option>, } +impl AsRef for ProgressUpdate { + fn as_ref(&self) -> &Self { + self + } +} + impl ProgressUpdate { /// Create an instance of `ProgressUpdate` for the job with this ID specifying its completion percentage. pub fn set(jid: JobId, percent: u8) -> ProgressUpdate { diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index 94fe13b8..1aaf2995 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -456,7 +456,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { .is_ok()); // Let's update the progress once again, to check the 'set_progress' shortcut: assert!(t - .set_progress(ProgressUpdate::set(job_id.clone(), 33)) + .set_progress(&ProgressUpdate::set(job_id.clone(), 33)) .await .is_ok()); @@ -465,7 +465,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { // ... and read the progress info let result = t - .get_progress(job_id.clone()) + .get_progress(&job_id) .await .expect("Retrieved progress update over the wire"); @@ -490,7 +490,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { let progress = t .lock() .expect("lock acquired successfully") - .get_progress(job_id.clone()) + .get_progress(&job_id) .await .expect("Retrieved progress update over the wire once again") .expect("Some progress"); @@ -510,12 +510,12 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { .desc("Final stage.".to_string()) .percent(99) .build(); - assert!(t.lock().unwrap().set_progress(upd).await.is_ok()); + assert!(t.lock().unwrap().set_progress(&upd).await.is_ok()); let progress = t .lock() .unwrap() - .get_progress(job_id) + .get_progress(&job_id) .await .expect("Retrieved progress update over the wire once again") .expect("Some progress"); @@ -523,7 +523,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { if progress.percent != Some(100) { let upd = progress.update_percent(100); assert_eq!(upd.desc, progress.desc); - assert!(t.lock().unwrap().set_progress(upd).await.is_ok()) + assert!(t.lock().unwrap().set_progress(&upd).await.is_ok()) } // What about 'ordinary' job ? @@ -538,7 +538,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { let progress = t .lock() .expect("lock acquired") - .get_progress(job_id.clone()) + .get_progress(&job_id) .await .expect("Retrieved progress update over the wire once again") .expect("Some progress"); From f947b60bf6b153bd1b08b777f42e105a49cdd54a Mon Sep 17 00:00:00 2001 From: --show-origin Date: Sun, 12 May 2024 22:32:49 +0500 Subject: [PATCH 124/129] Upd docs for ProgressUpdate --- src/proto/client/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 4b55870f..675b1361 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -120,10 +120,10 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{Client, JobId, ent::ProgressUpdateBuilder}; +/// use faktory::{Client, JobId, ent::ProgressUpdate}; /// let jid = JobId::new("W8qyVle9vXzUWQOf"); /// let mut cl = Client::connect(None).await?; -/// let progress = ProgressUpdateBuilder::new(jid) +/// let progress = ProgressUpdate::builder(jid) /// .desc("Almost done...".to_owned()) /// .percent(99) /// .build(); From d6c36e8a8863958ef616db7870c4bd68c6a08eb2 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 20 May 2024 11:28:27 +0400 Subject: [PATCH 125/129] Remove ServerState and Client::current_info logic --- src/lib.rs | 4 +- src/proto/client/mod.rs | 4 +- src/proto/mod.rs | 2 +- src/proto/single/resp.rs | 95 ++-------------------------------------- tests/real/community.rs | 85 ++++------------------------------- 5 files changed, 17 insertions(+), 173 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f8f97828..6f702741 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,9 +75,7 @@ mod proto; mod worker; pub use crate::error::Error; -pub use crate::proto::{ - Client, DataSnapshot, FaktoryState, Job, JobBuilder, JobId, Reconnect, ServerSnapshot, WorkerId, -}; +pub use crate::proto::{Client, Job, JobBuilder, JobId, Reconnect, WorkerId}; pub use crate::worker::{JobRunner, Worker, WorkerBuilder}; #[cfg(feature = "ent")] diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 675b1361..59e83037 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -372,10 +372,10 @@ where Ok((jobs_count - errors.len(), Some(errors))) } - /// Retrieve [information](crate::ServerSnapshot) about the running server. + /// Retrieve information about the running server. /// /// The returned value is the result of running the `INFO` command on the server. - pub async fn current_info(&mut self) -> Result { + pub async fn info(&mut self) -> Result { self.issue(&Info) .await? .read_json() diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 7d65ff6e..38822b74 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -9,7 +9,7 @@ pub(crate) use client::{ClientOptions, HeartbeatStatus, EXPECTED_PROTOCOL_VERSIO mod single; -pub use single::{DataSnapshot, FaktoryState, Job, JobBuilder, JobId, ServerSnapshot, WorkerId}; +pub use single::{Job, JobBuilder, JobId, WorkerId}; pub(crate) use single::{Ack, Fail, Info, Push, PushBulk, QueueAction, QueueControl}; diff --git a/src/proto/single/resp.rs b/src/proto/single/resp.rs index dba929b4..5d4ede59 100644 --- a/src/proto/single/resp.rs +++ b/src/proto/single/resp.rs @@ -1,11 +1,9 @@ -use crate::error::{self, Error}; -use chrono::{DateTime, Utc}; -use std::collections::HashMap; -use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncReadExt}; - #[cfg(feature = "ent")] use crate::ent::BatchId; +use crate::error::{self, Error}; +use tokio::io::AsyncBufRead; + pub fn bad(expected: &'static str, got: &RawResponse) -> error::Protocol { let stringy = match *got { RawResponse::String(ref s) => Some(&**s), @@ -120,92 +118,6 @@ pub async fn read_ok(r: R) -> Result<(), Error> { Err(bad("server ok", &rr).into()) } -// ---------------------------------------------- - -/// Faktory service stats. -#[derive(Serialize, Deserialize, Debug, Clone)] -#[non_exhaustive] -pub struct DataSnapshot { - /// Total number of job failures. - pub total_failures: u64, - - /// Total number of processed jobs. - pub total_processed: u64, - - /// Total number of enqueued jobs. - pub total_enqueued: u64, - - /// Total number of queues. - pub total_queues: u64, - - /// Queues stats. - /// - /// A mapping between a queue name and its size (number of jobs on the queue). - /// The keys of this map effectively make up a list of queues that are currently - /// registered in the Faktory service. - pub queues: HashMap, - - /// ***Deprecated***. Faktory's task runner stats. - /// - /// Note that this is exposed as a "generic" `serde_json::Value` since this info - /// belongs to deep implementation details of the Faktory service. - #[deprecated] - pub tasks: serde_json::Value, -} - -/// Faktory's server process stats. -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ServerSnapshot { - /// Faktory's description (e.g. "Faktory"). - pub description: String, - - /// Faktory's version as semver (e.g. "1.8.0"). - #[serde(rename = "faktory_version")] - pub version: String, - - /// Faktory server process uptime in seconds. - pub uptime: u64, - - /// Number of clients connected to the server. - pub connections: u64, - - /// Number of executed commands. - pub command_count: u64, - - /// Faktory server process memory usage. - pub used_memory_mb: u64, -} - -/// Current server state. -/// -/// Contains such details as how many queues there are on the server, statistics on the jobs, -/// as well as some specific info on server process memory usage, uptime, etc. -/// -/// Here is an example of the simplest way to fetch info on the server state. -/// ```no_run -/// # tokio_test::block_on(async { -/// use faktory::Client; -/// -/// let mut client = Client::connect(None).await.unwrap(); -/// let _server_state = client.current_info().await.unwrap(); -/// # }); -/// ``` -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct FaktoryState { - /// Server time. - pub now: DateTime, - - /// Server time as a string formatted as "%H:%M:%S UTC" (e.g. "19:47:39 UTC"). - pub server_utc_time: String, - - /// Faktory service stats. - #[serde(rename = "faktory")] - pub data: DataSnapshot, - - /// Faktory's server process stats. - pub server: ServerSnapshot, -} - // ---------------------------------------------- // // below is the implementation of the Redis RESP protocol @@ -220,6 +132,7 @@ pub enum RawResponse { Null, } +use tokio::io::{AsyncBufReadExt, AsyncReadExt}; async fn read(mut r: R) -> Result where R: AsyncBufRead + Unpin, diff --git a/tests/real/community.rs b/tests/real/community.rs index 59af0237..84080263 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -70,74 +70,6 @@ async fn roundtrip() { assert!(drained); } -#[tokio::test(flavor = "multi_thread")] -async fn server_state() { - skip_check!(); - - let local = "server_state"; - - // prepare a worker - let mut w = WorkerBuilder::default() - .register_fn(local, move |_| async move { Ok::<(), io::Error>(()) }) - .connect(None) - .await - .unwrap(); - - // prepare a producing client - let mut client = Client::connect(None).await.unwrap(); - - // examine server state before pushing anything - let server_state = client.current_info().await.unwrap(); - assert!(server_state.data.queues.get(local).is_none()); - // the following two assertions are not super-helpful but - // there is not much info we can make meaningful assetions on anyhow - // (like memusage, server description string, version, etc.) - assert!(server_state.server.connections >= 2); // at least two clients from the current test - assert!(server_state.server.uptime > 0); // if IPC is happenning, this should hold :) - - // push 1 job - client - .enqueue( - JobBuilder::new(local) - .args(vec!["abc"]) - .queue(local) - .build(), - ) - .await - .unwrap(); - - // we only pushed 1 job on this queue - let server_state = client.current_info().await.unwrap(); - assert_eq!(*server_state.data.queues.get(local).unwrap(), 1); - assert!(server_state.data.total_enqueued >= 1); // at least 1 job from this test - assert!(server_state.data.total_queues >= 1); // at least 1 qeueu from this test - // let's know consume that job ... - assert!(w.run_one(0, &[local]).await.unwrap()); - - // ... and verify the queue has got 0 pending jobs - // - // NB! If this is not passing locally, make sure to launch a fresh Faktory container, - // because if you have not pruned its volume the Faktory will still keep the queue name - // as registered. - // But generally, we are performing a clean-up by consuming the jobs from the local queue/ - // and then deleting the queue programmatically, so there is normally no need to prune docker - // volumes to perform the next test run. Also note that on CI we are always starting a-fresh. - let server_state = client.current_info().await.unwrap(); - assert_eq!(*server_state.data.queues.get(local).unwrap(), 0); - assert!(server_state.data.total_processed >= 1); // at least 1 job from this test - - client.queue_remove(&[local]).await.unwrap(); - - assert!(client - .current_info() - .await - .unwrap() - .data - .queues - .get(local) - .is_none()); -} - #[tokio::test(flavor = "multi_thread")] async fn multi() { skip_check!(); @@ -303,8 +235,8 @@ async fn queue_control_actions() { assert!(rx.try_recv().is_ok()); // let's inspect the sever state - let server_state = client.current_info().await.unwrap(); - let queues = &server_state.data.queues; + let server_state = client.info().await.unwrap(); + let queues = &server_state.get("faktory").unwrap().get("queues").unwrap(); assert_eq!(*queues.get(local_1).unwrap(), 1); // 1 job remaining assert_eq!(*queues.get(local_2).unwrap(), 1); // also 1 job remaining @@ -318,8 +250,8 @@ async fn queue_control_actions() { assert!(!rx.try_recv().is_ok()); // let's inspect the sever state again - let server_state = client.current_info().await.unwrap(); - let queues = &server_state.data.queues; + let server_state = client.info().await.unwrap(); + let queues = &server_state.get("data").unwrap().get("queues").unwrap(); // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); assert!(queues.get(local_2).is_none()); @@ -385,8 +317,8 @@ async fn queue_control_actions_wildcard() { assert!(rx.try_recv().is_ok()); // let's inspect the sever state - let server_state = client.current_info().await.unwrap(); - let queues = &server_state.data.queues; + let server_state = client.info().await.unwrap(); + let queues = &server_state.get("data").unwrap().get("queues").unwrap(); assert_eq!(*queues.get(local_1).unwrap(), 1); // 1 job remaining assert_eq!(*queues.get(local_2).unwrap(), 1); // also 1 job remaining @@ -400,8 +332,9 @@ async fn queue_control_actions_wildcard() { assert!(!rx.try_recv().is_ok()); // let's inspect the sever state again - let server_state = client.current_info().await.unwrap(); - let queues = &server_state.data.queues; + let server_state = client.info().await.unwrap(); + let queues = &server_state.get("data").unwrap().get("queues").unwrap(); + // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); assert!(queues.get(local_2).is_none()); From 125eed13c157b726ddf65098a957e2bb30c8219d Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 20 May 2024 11:32:37 +0400 Subject: [PATCH 126/129] Use 'faktory' as key to grab 'data' in tests::real::community --- tests/real/community.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/real/community.rs b/tests/real/community.rs index 84080263..20843318 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -251,7 +251,7 @@ async fn queue_control_actions() { // let's inspect the sever state again let server_state = client.info().await.unwrap(); - let queues = &server_state.get("data").unwrap().get("queues").unwrap(); + let queues = &server_state.get("faktory").unwrap().get("queues").unwrap(); // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); assert!(queues.get(local_2).is_none()); From 6519036733b22a5e3f87befabcc1479667862700 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 20 May 2024 11:33:55 +0400 Subject: [PATCH 127/129] Chekout Makefile and .gitignor to main --- .gitignore | 1 - Makefile | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index bcddc769..0f735146 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ **/*.rs.bk perf.* .vscode -mutants.* diff --git a/Makefile b/Makefile index e5221541..16a2ff95 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ faktory/tls: .PHONY: faktory/tls/kill faktory/tls/kill: - docker compose -f docker/compose.yml down -v + docker compose -f docker/compose.yml down .PHONY: test test: From 1a83b8b5ae15a7152458f86218757ba177e89798 Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 20 May 2024 11:53:22 +0400 Subject: [PATCH 128/129] Use 'faktory' as key to grab 'data' in tests::real::community --- tests/real/community.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/real/community.rs b/tests/real/community.rs index 20843318..3fcb78b8 100644 --- a/tests/real/community.rs +++ b/tests/real/community.rs @@ -318,7 +318,7 @@ async fn queue_control_actions_wildcard() { // let's inspect the sever state let server_state = client.info().await.unwrap(); - let queues = &server_state.get("data").unwrap().get("queues").unwrap(); + let queues = &server_state.get("faktory").unwrap().get("queues").unwrap(); assert_eq!(*queues.get(local_1).unwrap(), 1); // 1 job remaining assert_eq!(*queues.get(local_2).unwrap(), 1); // also 1 job remaining @@ -333,7 +333,7 @@ async fn queue_control_actions_wildcard() { // let's inspect the sever state again let server_state = client.info().await.unwrap(); - let queues = &server_state.get("data").unwrap().get("queues").unwrap(); + let queues = &server_state.get("faktory").unwrap().get("queues").unwrap(); // our queue are not even mentioned in the server report: assert!(queues.get(local_1).is_none()); From 3b337907731d49f7188faff319c03596928d1d3b Mon Sep 17 00:00:00 2001 From: --show-origin Date: Mon, 20 May 2024 12:05:24 +0400 Subject: [PATCH 129/129] Rm tech debt fixes that are now a separate PR --- src/proto/client/ent.rs | 32 +++++++++++------------- src/proto/client/mod.rs | 6 ++--- src/proto/mod.rs | 5 +--- src/proto/single/ent/cmd.rs | 43 +++++++++++++------------------- src/proto/single/ent/mod.rs | 2 +- src/proto/single/ent/progress.rs | 6 ----- tests/real/enterprise.rs | 14 +++++------ 7 files changed, 43 insertions(+), 65 deletions(-) diff --git a/src/proto/client/ent.rs b/src/proto/client/ent.rs index b5dcf72b..2b54269a 100644 --- a/src/proto/client/ent.rs +++ b/src/proto/client/ent.rs @@ -1,26 +1,20 @@ use super::super::batch::{CommitBatch, GetBatchStatus, OpenBatch}; -use super::super::{single, BatchStatus, JobId, Progress, ProgressUpdate}; +use super::super::{single, BatchStatus, JobId, Progress, ProgressUpdate, Track}; use super::{Client, ReadToken}; use crate::ent::{Batch, BatchHandle, BatchId}; use crate::error::{self, Error}; -use crate::proto::FetchProgress; use tokio::io::{AsyncBufRead, AsyncWrite}; impl Client { /// Send information on a job's execution progress to Faktory. - pub async fn set_progress

(&mut self, upd: P) -> Result<(), Error> - where - P: AsRef + Sync, - { - self.issue(&upd).await?.read_ok().await + pub async fn set_progress(&mut self, upd: ProgressUpdate) -> Result<(), Error> { + let cmd = Track::Set(upd); + self.issue(&cmd).await?.read_ok().await } /// Fetch information on a job's execution progress from Faktory. - pub async fn get_progress(&mut self, jid: J) -> Result, Error> - where - J: AsRef + Sync, - { - let cmd = FetchProgress::new(jid); + pub async fn get_progress(&mut self, jid: JobId) -> Result, Error> { + let cmd = Track::Get(jid); self.issue(&cmd).await?.read_json().await } @@ -67,13 +61,15 @@ impl<'a, S: AsyncBufRead + AsyncWrite + Unpin + Send> ReadToken<'a, S> { pub(crate) async fn maybe_bid(self) -> Result, Error> { match single::read_bid(&mut self.0.stream).await { Ok(bid) => Ok(Some(bid)), - Err(Error::Protocol(error::Protocol::Internal { msg })) => { - if msg.starts_with("No such batch") { - return Ok(None); + Err(err) => match err { + Error::Protocol(error::Protocol::Internal { msg }) => { + if msg.starts_with("No such batch") { + return Ok(None); + } + Err(error::Protocol::Internal { msg }.into()) } - Err(error::Protocol::Internal { msg }.into()) - } - Err(another) => Err(another), + another => Err(another), + }, } } } diff --git a/src/proto/client/mod.rs b/src/proto/client/mod.rs index 59e83037..b68054bc 100644 --- a/src/proto/client/mod.rs +++ b/src/proto/client/mod.rs @@ -120,14 +120,14 @@ fn check_protocols_match(ver: usize) -> Result<(), Error> { /// /// ```no_run /// # tokio_test::block_on(async { -/// use faktory::{Client, JobId, ent::ProgressUpdate}; +/// use faktory::{Client, JobId, ent::ProgressUpdateBuilder}; /// let jid = JobId::new("W8qyVle9vXzUWQOf"); /// let mut cl = Client::connect(None).await?; -/// let progress = ProgressUpdate::builder(jid) +/// let progress = ProgressUpdateBuilder::new(jid) /// .desc("Almost done...".to_owned()) /// .percent(99) /// .build(); -/// cl.set_progress(&progress).await?; +/// cl.set_progress(progress).await?; /// # Ok::<(), faktory::Error>(()) /// }); ///```` diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 38822b74..5768b085 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -16,10 +16,7 @@ pub(crate) use single::{Ack, Fail, Info, Push, PushBulk, QueueAction, QueueContr pub(crate) mod utils; #[cfg(feature = "ent")] -pub use self::single::ent::{JobState, Progress, ProgressUpdate, ProgressUpdateBuilder}; - -#[cfg(feature = "ent")] -pub(crate) use self::single::ent::FetchProgress; +pub use self::single::ent::{JobState, Progress, ProgressUpdate, ProgressUpdateBuilder, Track}; #[cfg(feature = "ent")] pub use self::single::BatchId; diff --git a/src/proto/single/ent/cmd.rs b/src/proto/single/ent/cmd.rs index 8edb5a88..43881e44 100644 --- a/src/proto/single/ent/cmd.rs +++ b/src/proto/single/ent/cmd.rs @@ -3,36 +3,27 @@ use crate::error::Error; use crate::proto::{single::FaktoryCommand, JobId}; use tokio::io::{AsyncWrite, AsyncWriteExt}; -#[async_trait::async_trait] -impl

FaktoryCommand for P -where - P: AsRef + Sync, -{ - async fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"TRACK SET ").await?; - let r = serde_json::to_vec(self.as_ref()).map_err(Error::Serialization)?; - w.write_all(&r).await?; - Ok(w.write_all(b"\r\n").await?) - } -} - #[derive(Debug, Clone)] -pub(crate) struct FetchProgress(J); - -impl FetchProgress { - pub fn new(j: J) -> Self { - Self(j) - } +pub enum Track { + Set(ProgressUpdate), + Get(JobId), } #[async_trait::async_trait] -impl FaktoryCommand for FetchProgress -where - J: AsRef + Sync, -{ +impl FaktoryCommand for Track { async fn issue(&self, w: &mut W) -> Result<(), Error> { - w.write_all(b"TRACK GET ").await?; - w.write_all(self.0.as_ref().as_bytes()).await?; - Ok(w.write_all(b"\r\n").await?) + match self { + Self::Set(upd) => { + w.write_all(b"TRACK SET ").await?; + let r = serde_json::to_vec(upd).map_err(Error::Serialization)?; + w.write_all(&r).await?; + Ok(w.write_all(b"\r\n").await?) + } + Self::Get(jid) => { + w.write_all(b"TRACK GET ").await?; + w.write_all(jid.as_bytes()).await?; + Ok(w.write_all(b"\r\n").await?) + } + } } } diff --git a/src/proto/single/ent/mod.rs b/src/proto/single/ent/mod.rs index 2a017c49..e6f4f63e 100644 --- a/src/proto/single/ent/mod.rs +++ b/src/proto/single/ent/mod.rs @@ -5,7 +5,7 @@ mod cmd; mod progress; mod utils; -pub(crate) use cmd::FetchProgress; +pub use cmd::Track; pub use progress::{JobState, Progress, ProgressUpdate, ProgressUpdateBuilder}; impl JobBuilder { diff --git a/src/proto/single/ent/progress.rs b/src/proto/single/ent/progress.rs index f7ca87d4..49304a5a 100644 --- a/src/proto/single/ent/progress.rs +++ b/src/proto/single/ent/progress.rs @@ -38,12 +38,6 @@ pub struct ProgressUpdate { pub reserve_until: Option>, } -impl AsRef for ProgressUpdate { - fn as_ref(&self) -> &Self { - self - } -} - impl ProgressUpdate { /// Create an instance of `ProgressUpdate` for the job with this ID specifying its completion percentage. pub fn set(jid: JobId, percent: u8) -> ProgressUpdate { diff --git a/tests/real/enterprise.rs b/tests/real/enterprise.rs index 1aaf2995..94fe13b8 100644 --- a/tests/real/enterprise.rs +++ b/tests/real/enterprise.rs @@ -456,7 +456,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { .is_ok()); // Let's update the progress once again, to check the 'set_progress' shortcut: assert!(t - .set_progress(&ProgressUpdate::set(job_id.clone(), 33)) + .set_progress(ProgressUpdate::set(job_id.clone(), 33)) .await .is_ok()); @@ -465,7 +465,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { // ... and read the progress info let result = t - .get_progress(&job_id) + .get_progress(job_id.clone()) .await .expect("Retrieved progress update over the wire"); @@ -490,7 +490,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { let progress = t .lock() .expect("lock acquired successfully") - .get_progress(&job_id) + .get_progress(job_id.clone()) .await .expect("Retrieved progress update over the wire once again") .expect("Some progress"); @@ -510,12 +510,12 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { .desc("Final stage.".to_string()) .percent(99) .build(); - assert!(t.lock().unwrap().set_progress(&upd).await.is_ok()); + assert!(t.lock().unwrap().set_progress(upd).await.is_ok()); let progress = t .lock() .unwrap() - .get_progress(&job_id) + .get_progress(job_id) .await .expect("Retrieved progress update over the wire once again") .expect("Some progress"); @@ -523,7 +523,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { if progress.percent != Some(100) { let upd = progress.update_percent(100); assert_eq!(upd.desc, progress.desc); - assert!(t.lock().unwrap().set_progress(&upd).await.is_ok()) + assert!(t.lock().unwrap().set_progress(upd).await.is_ok()) } // What about 'ordinary' job ? @@ -538,7 +538,7 @@ async fn test_tracker_can_send_and_retrieve_job_execution_progress() { let progress = t .lock() .expect("lock acquired") - .get_progress(&job_id) + .get_progress(job_id.clone()) .await .expect("Retrieved progress update over the wire once again") .expect("Some progress");